@redocly/reef 0.132.0-next.4 → 0.132.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/client/app/CircularProgress.js +8 -8
  3. package/dist/server/config/env-schema.d.ts +3 -0
  4. package/dist/server/config/env-schemas/auth.d.ts +3 -0
  5. package/dist/server/config/env-schemas/auth.js +1 -1
  6. package/dist/server/plugins/catalog-entities/plugin.js +1 -1
  7. package/dist/server/plugins/config-parser/index.js +1 -1
  8. package/dist/server/plugins/dev-onboarding/api/adapter-factory.d.ts +10 -3
  9. package/dist/server/plugins/dev-onboarding/api/adapter-factory.js +1 -1
  10. package/dist/server/plugins/dev-onboarding/api/adapters/apigee/adapter.d.ts +5 -4
  11. package/dist/server/plugins/dev-onboarding/api/adapters/apigee/adapter.js +4 -4
  12. package/dist/server/plugins/dev-onboarding/api/adapters/gravitee/client.d.ts +3 -3
  13. package/dist/server/plugins/dev-onboarding/api/adapters/gravitee/client.js +1 -1
  14. package/dist/server/plugins/dev-onboarding/api/middlewares/integrations.d.ts +3 -2
  15. package/dist/server/plugins/dev-onboarding/api/middlewares/integrations.js +1 -1
  16. package/dist/server/plugins/dev-onboarding/api/routes/index.js +1 -1
  17. package/dist/server/plugins/dev-onboarding/api/types.d.ts +8 -0
  18. package/dist/server/plugins/dev-onboarding/template/components/ApiLogsActions.js +5 -5
  19. package/dist/server/plugins/dev-onboarding/template/components/DialogStyledComponents.js +1 -1
  20. package/dist/server/plugins/dev-onboarding/template/components/MultiSelect.js +1 -1
  21. package/dist/server/plugins/dev-onboarding/template/components/TextField.js +3 -3
  22. package/dist/server/plugins/enforce-login/index.js +1 -1
  23. package/dist/server/plugins/lifecycle.js +2 -2
  24. package/dist/server/plugins/markdown/search/to-markdown.js +24 -16
  25. package/dist/server/plugins/markdown/search/walk-sections.js +1 -1
  26. package/dist/server/plugins/openapi-docs/index.js +1 -1
  27. package/dist/server/plugins/openapi-docs/search-indexer.js +1 -1
  28. package/dist/server/store.d.ts +2 -2
  29. package/dist/server/store.js +1 -1
  30. package/dist/server/types/plugins/common.d.ts +5 -2
  31. package/dist/server/types/redirects.d.ts +6 -0
  32. package/dist/server/utils/redirects/find-redirect.d.ts +8 -31
  33. package/dist/server/utils/redirects/find-redirect.js +1 -1
  34. package/dist/server/utils/redirects/follow-redirect-chain.d.ts +9 -0
  35. package/dist/server/utils/redirects/follow-redirect-chain.js +1 -0
  36. package/dist/server/utils/redirects/validate-redirects.d.ts +8 -0
  37. package/dist/server/utils/redirects/validate-redirects.js +1 -0
  38. package/dist/types/global-data.d.ts +7 -2
  39. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @redocly/reef
2
2
 
3
+ ## 0.132.0-next.6
4
+
5
+ ### Minor Changes
6
+
7
+ - 07483ec0596: Added RBAC filtering for Apigee API products in developer onboarding app creation.
8
+ - e756f2aad58: Added a `palette` configuration option that applies one of built-in color palettes to the project.
9
+
10
+ ### Patch Changes
11
+
12
+ - e4e8c9f42d4: Updated legacy palette styles.
13
+ - c336f96cb65: Updated `@redocly/openapi-core` to version `2.25.2`.
14
+ - cd3d136f0c2: Fixed an issue where `json-schema` Markdoc tags produced empty output in LLM content by rendering resolved JSON examples as code blocks.
15
+ - Updated dependencies [e4e8c9f42d4]
16
+ - Updated dependencies [f69e101a44b]
17
+ - Updated dependencies [c336f96cb65]
18
+ - Updated dependencies [037535441a2]
19
+ - Updated dependencies [1d1c2056a3c]
20
+ - Updated dependencies [e756f2aad58]
21
+ - @redocly/theme@0.64.0-next.3
22
+ - @redocly/openapi-docs@3.20.0-next.5
23
+ - @redocly/asyncapi-docs@1.9.0-next.5
24
+ - @redocly/graphql-docs@1.9.0-next.5
25
+ - @redocly/portal-plugin-mock-server@0.17.0-next.5
26
+
27
+ ## 0.132.0-next.5
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [ac77c65652f]
32
+ - @redocly/asyncapi-docs@1.9.0-next.4
33
+ - @redocly/graphql-docs@1.9.0-next.4
34
+ - @redocly/openapi-docs@3.20.0-next.4
35
+ - @redocly/portal-plugin-mock-server@0.17.0-next.4
36
+
3
37
  ## 0.132.0-next.4
4
38
 
5
39
  ### Patch Changes
@@ -1,19 +1,19 @@
1
- import*as r from"react";import i from"styled-components";function t(e){return r.createElement(n,{...e},r.createElement("div",null),r.createElement("div",null),r.createElement("div",null),r.createElement("div",null))}const n=i.div`
1
+ import*as e from"react";import i from"styled-components";function t(r){return e.createElement(a,{...r},e.createElement("div",null),e.createElement("div",null),e.createElement("div",null),e.createElement("div",null))}const a=i.div`
2
2
  display: inline-block;
3
3
  position: relative;
4
- width: ${({size:e})=>e||"60"}px;
5
- height: ${({size:e})=>e||"60"}px;
4
+ width: ${({size:r})=>r||"60"}px;
5
+ height: ${({size:r})=>r||"60"}px;
6
6
  div {
7
7
  box-sizing: border-box;
8
8
  display: block;
9
9
  position: absolute;
10
- width: ${({size:e})=>e||"44"}px;
11
- height: ${({size:e})=>e||"44"}px;
10
+ width: ${({size:r})=>r||"44"}px;
11
+ height: ${({size:r})=>r||"44"}px;
12
12
  margin: 0;
13
- border: 2px solid var(--color-primary-base);
13
+ border: 2px solid var(--color-primary-base, var(--color-blueberry-6));
14
14
  border-radius: 50%;
15
15
  animation: ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
16
- border-color: var(--color-primary-base) transparent transparent transparent;
16
+ border-color: var(--color-primary-base, var(--color-blueberry-6)) transparent transparent transparent;
17
17
  }
18
18
  div:nth-child(1) {
19
19
  animation-delay: -0.45s;
@@ -32,4 +32,4 @@ import*as r from"react";import i from"styled-components";function t(e){return r.
32
32
  transform: rotate(360deg);
33
33
  }
34
34
  }
35
- `;export{n as StyledSpinner,t as default};
35
+ `;export{a as StyledSpinner,t as default};
@@ -42,6 +42,7 @@ export declare const envSchema: z.ZodObject<{
42
42
  LOCALHOST_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
43
43
  REDOCLY_OAUTH_USE_INTROSPECT: z.ZodOptional<z.ZodString>;
44
44
  REDOCLY_ENFORCE_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
45
+ REDOCLY_ENFORCE_RESIDENCY: z.ZodOptional<z.ZodString>;
45
46
  } & {
46
47
  REDOCLY_SSR_RENDER_MODE: z.ZodOptional<z.ZodEnum<["worker", "main"]>>;
47
48
  REDOCLY_SSR_WORKERS_MIN: z.ZodOptional<z.ZodNumber>;
@@ -123,6 +124,7 @@ export declare const envSchema: z.ZodObject<{
123
124
  LOCALHOST_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
124
125
  REDOCLY_OAUTH_USE_INTROSPECT: z.ZodOptional<z.ZodString>;
125
126
  REDOCLY_ENFORCE_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
127
+ REDOCLY_ENFORCE_RESIDENCY: z.ZodOptional<z.ZodString>;
126
128
  } & {
127
129
  REDOCLY_SSR_RENDER_MODE: z.ZodOptional<z.ZodEnum<["worker", "main"]>>;
128
130
  REDOCLY_SSR_WORKERS_MIN: z.ZodOptional<z.ZodNumber>;
@@ -204,6 +206,7 @@ export declare const envSchema: z.ZodObject<{
204
206
  LOCALHOST_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
205
207
  REDOCLY_OAUTH_USE_INTROSPECT: z.ZodOptional<z.ZodString>;
206
208
  REDOCLY_ENFORCE_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
209
+ REDOCLY_ENFORCE_RESIDENCY: z.ZodOptional<z.ZodString>;
207
210
  } & {
208
211
  REDOCLY_SSR_RENDER_MODE: z.ZodOptional<z.ZodEnum<["worker", "main"]>>;
209
212
  REDOCLY_SSR_WORKERS_MIN: z.ZodOptional<z.ZodNumber>;
@@ -15,6 +15,7 @@ export declare const authSchema: z.ZodObject<{
15
15
  LOCALHOST_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
16
16
  REDOCLY_OAUTH_USE_INTROSPECT: z.ZodOptional<z.ZodString>;
17
17
  REDOCLY_ENFORCE_LOGIN: z.ZodOptional<z.ZodEnum<["true", "false"]>>;
18
+ REDOCLY_ENFORCE_RESIDENCY: z.ZodOptional<z.ZodString>;
18
19
  }, "strip", z.ZodTypeAny, {
19
20
  JWT_SECRET_KEY?: string | undefined;
20
21
  AUTH_URL?: string | undefined;
@@ -28,6 +29,7 @@ export declare const authSchema: z.ZodObject<{
28
29
  LOCALHOST_LOGIN?: "true" | "false" | undefined;
29
30
  REDOCLY_OAUTH_USE_INTROSPECT?: string | undefined;
30
31
  REDOCLY_ENFORCE_LOGIN?: "true" | "false" | undefined;
32
+ REDOCLY_ENFORCE_RESIDENCY?: string | undefined;
31
33
  }, {
32
34
  JWT_SECRET_KEY?: string | undefined;
33
35
  AUTH_URL?: string | undefined;
@@ -41,5 +43,6 @@ export declare const authSchema: z.ZodObject<{
41
43
  LOCALHOST_LOGIN?: "true" | "false" | undefined;
42
44
  REDOCLY_OAUTH_USE_INTROSPECT?: string | undefined;
43
45
  REDOCLY_ENFORCE_LOGIN?: "true" | "false" | undefined;
46
+ REDOCLY_ENFORCE_RESIDENCY?: string | undefined;
44
47
  }>;
45
48
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- import{z as o}from"zod";const n=o.object({JWT_SECRET_KEY:o.string().optional(),AUTH_URL:o.string().url().optional(),BH_API_URL:o.string().url().optional(),ENTITLEMENTS_JWKS_CDN_URL:o.string().url().optional(),OAUTH_CLIENT_ID:o.string().optional(),OAUTH_CLIENT_SECRET:o.string().optional(),OIDC_CLIENT_ID:o.string().optional(),OIDC_CLIENT_SECRET:o.string().optional(),OIDC_ISSUER_URL:o.string().url().optional(),LOCALHOST_LOGIN:o.enum(["true","false"]).optional(),REDOCLY_OAUTH_USE_INTROSPECT:o.string().optional(),REDOCLY_ENFORCE_LOGIN:o.enum(["true","false"]).optional()});export{n as authSchema};
1
+ import{z as o}from"zod";const n=o.object({JWT_SECRET_KEY:o.string().optional(),AUTH_URL:o.string().url().optional(),BH_API_URL:o.string().url().optional(),ENTITLEMENTS_JWKS_CDN_URL:o.string().url().optional(),OAUTH_CLIENT_ID:o.string().optional(),OAUTH_CLIENT_SECRET:o.string().optional(),OIDC_CLIENT_ID:o.string().optional(),OIDC_CLIENT_SECRET:o.string().optional(),OIDC_ISSUER_URL:o.string().url().optional(),LOCALHOST_LOGIN:o.enum(["true","false"]).optional(),REDOCLY_OAUTH_USE_INTROSPECT:o.string().optional(),REDOCLY_ENFORCE_LOGIN:o.enum(["true","false"]).optional(),REDOCLY_ENFORCE_RESIDENCY:o.string().url().optional()});export{n as authSchema};
@@ -1 +1 @@
1
- import{FileType as p}from"../../persistence/file-hashes/types.js";import{envConfig as P}from"../../config/env-config.js";import{deepEqual as I}from"../../../utils/object/deep-equal.js";import{CATALOG_BASE_SLUG as u}from"../../../constants/catalog-entities.js";import{telemetryTraceStep as D}from"../../../cli/telemetry/helpers/trace-step.js";import{catalogDataCollector as O}from"./utils/catalog-data-collector.js";import{CATALOG_FILTERS_CACHE_NAMESPACE as b,ENTITIES_MAP_GLOBAL_DATA_KEY as _}from"../../constants/plugins/catalog-entities.js";import{CacheService as v}from"../../persistence/cache/services/cache-service.js";import{getTemplatePath as f}from"./utils/get-template-path.js";import{getCompleteCatalogConfig as E}from"./get-complete-catalog-config.js";import{AsyncApiEntitiesExtractor as w}from"./extensions/extractors/api-description/asyncapi-entities-extractor.js";import{GraphqlEntitiesExtractor as R}from"./extensions/extractors/api-description/graphql-entities-extractor.js";import{FileHashesService as N}from"../../persistence/file-hashes/services/file-hashes-service.js";import{CatalogEntitiesService as L}from"./database/catalog-entities-service.js";import{ArazzoEntitiesExtractor as G}from"./extensions/extractors/api-description/arazzo-entities-extractor.js";import{FsEntitiesExtractor as x}from"./extensions/extractors/fs-entities-extractor.js";import{HashManager as F}from"./utils/hash-manager.js";import{OpenApiEntitiesExtractor as H}from"./extensions/extractors/api-description/openapi-entities-extractor.js";const B="catalog-entity-template",M="catalog-entity";let C=!0,d;async function Y(){return{id:"CatalogEntities",requiredEntitlements:["catalog"],async processContent(t,e){const a=await e.getConfig(),r=E(a.entitiesCatalog);if(!r.show)return;const{logger:o}=e,n=t.registerServerPropsGetter(M,f("../get-server-props.js")),g=t.createTemplate(B,f("../template/index.js"));t.addRoute({duplicateInAllLocales:!0,slug:u,fsPath:"",templateId:g,excludeFromSidebar:!0,hasClientRoutes:!0,serverPropsGetterIds:[n],getNavText:()=>Promise.resolve("Catalog"),getStaticData:async()=>({props:{catalogConfig:r}})});const[m]=Object.entries(r.catalogs??{}).find(([s,i])=>!i?.hide)||[];m&&t.addRedirect(u,{type:302,to:`${u}/${m}`}),o.info("Catalog Entities plugin finished")},async afterRoutesCreated(t,e){await D("build.plugin.catalog_entities",async a=>{const r=await e.getConfig(),o=E(r.entitiesCatalog);if(a?.setAttribute("config",JSON.stringify(o)),!o.show)return;const{logger:n}=e,g=C&&P.isDevelopMode,m=!I(d,r.access?.rbac);d=r.access?.rbac;const s=g||m,i=await L.getInstance({baseDbDir:t.serverOutDir,removeExisting:g,runOnlyLocalDatabase:!0,runWithPragmaWalWriteOptimization:!0}),A=await N.getInstance({baseDbDir:t.serverOutDir}),l=new F(A),T=[new x({fileHashManager:l,context:e,catalogEntitiesService:i,catalogConfig:o,shouldCalculateEntities:s}),new H({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:p.OPENAPI_DESCRIPTION,shouldCalculateEntities:s}),new w({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:p.ASYNCAPI_DESCRIPTION,shouldCalculateEntities:s}),new R({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:p.GRAPHQL_DESCRIPTION,shouldCalculateEntities:s}),new G({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:p.ARAZZO_DESCRIPTION,shouldCalculateEntities:s})];n.info("Starting entities extractors...");const y=n.startTiming();await i.transaction(async()=>{await Promise.all(T.map(async S=>S.extract()))});const h=i.getEntitySources();t.setGlobalData({[_]:h}),await(await v.getInstance({baseDbDir:t.serverOutDir})).deleteByNamespace(b),n.infoTime(y,"Entities extractors finished");const c=await O.getCatalogEntitiesData(i);a?.setAttribute("totalEntities",c.totalEntitiesCount),a?.setAttribute("entitiesCountByType",JSON.stringify(c.countOfEntitiesByType)),a?.setAttribute("totalFilesSkippedByHash",c.totalFilesSkippedByHash),a?.setAttribute("totalProcessedFiles",c.totalProcessedFiles),a?.setAttribute("extractors",c.extractors),C=!1})}}}var ot=Y;export{Y as catalogEntitiesPlugin,ot as default};
1
+ import{FileType as m}from"../../persistence/file-hashes/types.js";import{envConfig as P}from"../../config/env-config.js";import{deepEqual as I}from"../../../utils/object/deep-equal.js";import{CATALOG_BASE_SLUG as p}from"../../../constants/catalog-entities.js";import{telemetryTraceStep as D}from"../../../cli/telemetry/helpers/trace-step.js";import{catalogDataCollector as O}from"./utils/catalog-data-collector.js";import{CATALOG_FILTERS_CACHE_NAMESPACE as b,ENTITIES_MAP_GLOBAL_DATA_KEY as _}from"../../constants/plugins/catalog-entities.js";import{CacheService as v}from"../../persistence/cache/services/cache-service.js";import{getTemplatePath as f}from"./utils/get-template-path.js";import{getCompleteCatalogConfig as E}from"./get-complete-catalog-config.js";import{AsyncApiEntitiesExtractor as w}from"./extensions/extractors/api-description/asyncapi-entities-extractor.js";import{GraphqlEntitiesExtractor as R}from"./extensions/extractors/api-description/graphql-entities-extractor.js";import{FileHashesService as N}from"../../persistence/file-hashes/services/file-hashes-service.js";import{CatalogEntitiesService as L}from"./database/catalog-entities-service.js";import{ArazzoEntitiesExtractor as G}from"./extensions/extractors/api-description/arazzo-entities-extractor.js";import{FsEntitiesExtractor as x}from"./extensions/extractors/fs-entities-extractor.js";import{HashManager as F}from"./utils/hash-manager.js";import{OpenApiEntitiesExtractor as H}from"./extensions/extractors/api-description/openapi-entities-extractor.js";const B="catalog-entity-template",M="catalog-entity";let C=!0,d;async function k(){return{id:"CatalogEntities",requiredEntitlements:["catalog"],async processContent(t,e){const a=await e.getConfig(),r=E(a.entitiesCatalog);if(!r.show)return;const{logger:o}=e,n=t.registerServerPropsGetter(M,f("../get-server-props.js")),g=t.createTemplate(B,f("../template/index.js"));t.addRoute({duplicateInAllLocales:!0,slug:p,fsPath:"",templateId:g,excludeFromSidebar:!0,hasClientRoutes:!0,serverPropsGetterIds:[n],getNavText:()=>Promise.resolve("Catalog"),getStaticData:async()=>({props:{catalogConfig:r}})});const[u]=Object.entries(r.catalogs??{}).find(([s,i])=>!i?.hide)||[];u&&t.addRedirect(p,{type:302,to:`${p}/${u}`},{trackOriginalSource:!1}),o.info("Catalog Entities plugin finished")},async afterRoutesCreated(t,e){await D("build.plugin.catalog_entities",async a=>{const r=await e.getConfig(),o=E(r.entitiesCatalog);if(a?.setAttribute("config",JSON.stringify(o)),!o.show)return;const{logger:n}=e,g=C&&P.isDevelopMode,u=!I(d,r.access?.rbac);d=r.access?.rbac;const s=g||u,i=await L.getInstance({baseDbDir:t.serverOutDir,removeExisting:g,runOnlyLocalDatabase:!0,runWithPragmaWalWriteOptimization:!0}),A=await N.getInstance({baseDbDir:t.serverOutDir}),l=new F(A),T=[new x({fileHashManager:l,context:e,catalogEntitiesService:i,catalogConfig:o,shouldCalculateEntities:s}),new H({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:m.OPENAPI_DESCRIPTION,shouldCalculateEntities:s}),new w({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:m.ASYNCAPI_DESCRIPTION,shouldCalculateEntities:s}),new R({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:m.GRAPHQL_DESCRIPTION,shouldCalculateEntities:s}),new G({actions:t,context:e,catalogEntitiesService:i,fileHashManager:l,fileType:m.ARAZZO_DESCRIPTION,shouldCalculateEntities:s})];n.info("Starting entities extractors...");const y=n.startTiming();await i.transaction(async()=>{await Promise.all(T.map(async h=>h.extract()))});const S=i.getEntitySources();t.setGlobalData({[_]:S}),await(await v.getInstance({baseDbDir:t.serverOutDir})).deleteByNamespace(b),n.infoTime(y,"Entities extractors finished");const c=await O.getCatalogEntitiesData(i);a?.setAttribute("totalEntities",c.totalEntitiesCount),a?.setAttribute("entitiesCountByType",JSON.stringify(c.countOfEntitiesByType)),a?.setAttribute("totalFilesSkippedByHash",c.totalFilesSkippedByHash),a?.setAttribute("totalProcessedFiles",c.totalProcessedFiles),a?.setAttribute("extractors",c.extractors),C=!1})}}}var ot=k;export{k as catalogEntitiesPlugin,ot as default};
@@ -1 +1 @@
1
- import A from"path";import{REDOCLY_ROUTE_RBAC as m,REDOCLY_TEAMS_RBAC as b}from"@redocly/config";import{deepMerge as v}from"../../../utils/object/deep-merge.js";import{buildWildcardRedirectsTree as P}from"../../utils/redirects/build-wildcard-redirects-tree.js";import{shaHexShort as S}from"../../utils/crypto/sha-hex-short.js";import{formatCustomScripts as u,formatCustomLinks as D}from"./format-custom-tags.js";import{applyL10nToRbacConfig as T,resolveDirectoryHashes as w}from"../../utils/rbac.js";import{copySeoAssets as L}from"./copy-seo-assets.js";import{normalizeRedirectSources as O}from"./normalize-redirect-sources.js";import{applyL10nToRedirects as k}from"./apply-l10n-to-redirects.js";import{DEFAULT_LOADERS as E}from"./loaders/index.js";import{resolveSearchFacets as G}from"./resolve-search-facets.js";import{telemetryTraceStep as _}from"../../../cli/telemetry/helpers/trace-step.js";import{transformMdAst as B}from"../markdown/runtime-transform.js";async function Q(){return{id:"Config Parser",loaders:E,async processContent(e,{getConfig:i,fs:t}){await _("build.plugin.config_parser",async o=>{const{contentDir:s,setGlobalConfig:a,setGlobalData:l}=e,r=await i(),n=r.access||{},p=T(t,n.rbac||{}),f={outdir:e.outdir,contentDir:s},y=await u(r.scripts?.head,f),h=await D(r.links,f),R=await u(r.scripts?.body,f),d=v(r,{headScriptTags:y,linkTags:h,postBodyScriptTags:R});o?.setAttribute("config",JSON.stringify(d));const g=r.redirects?O(r.redirects):{},C=d.banner?.map(c=>typeof c=="object"&&c!==null&&c.rbac?{...c,[b]:c.rbac}:c);a({...d,banner:C,access:{...n,requiresLogin:!!n.requiresLogin,rbac:p},directoryPaths:await w(t,n.rbac),seo:r.seo&&await L(r.seo,f.contentDir,f.outdir),redirects:k(g,r.l10n),wildcardRedirectsTree:P(g)}),G(r.search?.filters?.facets||[],e.getSearchFacets,e.setSearchFacets),l({removeAttribution:!!r.removeAttribution})})},async afterRoutesCreated(e,i){x(e);const t=e.getGlobalConfig("banner");if(Array.isArray(t)){const o=await Promise.all(t.map(async(s,a)=>{const{content:l,...r}=s,{ast:n}=await e.parseMarkdoc({input:{content:l,relativePath:"redocly.yaml"},context:i,resource:`redocly.yaml#banner${a}`}),{renderableAst:p}=await B(n,{variables:{},partials:{}},e.serverOutDir);return{...r,ast:p,hash:S(l)}}));e.setGlobalConfig({banner:o})}}}}function x(e){const i=e.getGlobalConfig("access")?.rbac,t=e.getGlobalConfig("directoryPaths");if(!(!i||Object.keys(i).length===0))for(const o of e.getAllRoutes()){if(o.versions&&t)for(const s of o.versions){const a=t[s.folderId],r=a==="."||a==="/"?"":a;s[m]={slug:s.link,fsPath:A.posix.join(r,"@"+s.version,"index.md")}}o[m]||o[b]||(o[m]={slug:o.slug,fsPath:o.fsPath})}}export{Q as configParserPlugin};
1
+ import S from"path";import{REDOCLY_ROUTE_RBAC as g,REDOCLY_TEAMS_RBAC as b}from"@redocly/config";import{deepMerge as v}from"../../../utils/object/deep-merge.js";import{buildWildcardRedirectsTree as P}from"../../utils/redirects/build-wildcard-redirects-tree.js";import{shaHexShort as D}from"../../utils/crypto/sha-hex-short.js";import{formatCustomScripts as u,formatCustomLinks as T}from"./format-custom-tags.js";import{applyL10nToRbacConfig as w,resolveDirectoryHashes as L}from"../../utils/rbac.js";import{copySeoAssets as O}from"./copy-seo-assets.js";import{normalizeRedirectSources as k}from"./normalize-redirect-sources.js";import{applyL10nToRedirects as E}from"./apply-l10n-to-redirects.js";import{DEFAULT_LOADERS as G}from"./loaders/index.js";import{resolveSearchFacets as _}from"./resolve-search-facets.js";import{telemetryTraceStep as B}from"../../../cli/telemetry/helpers/trace-step.js";import{transformMdAst as j}from"../markdown/runtime-transform.js";async function V(){return{id:"Config Parser",loaders:G,async processContent(e,{getConfig:i,fs:t}){await B("build.plugin.config_parser",async o=>{const{contentDir:s,setGlobalConfig:a,setGlobalData:l}=e,r=await i(),n=r.access||{},d=w(t,n.rbac||{}),f={outdir:e.outdir,contentDir:s},y=await u(r.scripts?.head,f),h=await T(r.links,f),R=await u(r.scripts?.body,f),p=v(r,{headScriptTags:y,linkTags:h,postBodyScriptTags:R});o?.setAttribute("config",JSON.stringify(p));const m=r.redirects?k(r.redirects):{},C=Object.keys(m),A=p.banner?.map(c=>typeof c=="object"&&c!==null&&c.rbac?{...c,[b]:c.rbac}:c);a({...p,banner:A,access:{...n,requiresLogin:!!n.requiresLogin,rbac:d},directoryPaths:await L(t,n.rbac),seo:r.seo&&await O(r.seo,f.contentDir,f.outdir),redirects:E(m,r.l10n),wildcardRedirectsTree:P(m),originalRedirectSources:C}),_(r.search?.filters?.facets||[],e.getSearchFacets,e.setSearchFacets),l({removeAttribution:!!r.removeAttribution})})},async afterRoutesCreated(e,i){x(e);const t=e.getGlobalConfig("banner");if(Array.isArray(t)){const o=await Promise.all(t.map(async(s,a)=>{const{content:l,...r}=s,{ast:n}=await e.parseMarkdoc({input:{content:l,relativePath:"redocly.yaml"},context:i,resource:`redocly.yaml#banner${a}`}),{renderableAst:d}=await j(n,{variables:{},partials:{}},e.serverOutDir);return{...r,ast:d,hash:D(l)}}));e.setGlobalConfig({banner:o})}}}}function x(e){const i=e.getGlobalConfig("access")?.rbac,t=e.getGlobalConfig("directoryPaths");if(!(!i||Object.keys(i).length===0))for(const o of e.getAllRoutes()){if(o.versions&&t)for(const s of o.versions){const a=t[s.folderId],r=a==="."||a==="/"?"":a;s[g]={slug:s.link,fsPath:S.posix.join(r,"@"+s.version,"index.md")}}o[g]||o[b]||(o[g]={slug:o.slug,fsPath:o.fsPath})}}export{V as configParserPlugin};
@@ -1,6 +1,13 @@
1
- import type { Adapter, UserClaims } from './types';
2
- import type { DevOnboardingAdapterConfig } from '@redocly/config';
1
+ import type { Adapter, CatalogApiProduct, UserClaims } from './types';
2
+ import type { DevOnboardingAdapterConfig, RbacConfig } from '@redocly/config';
3
+ export type AdapterInput = {
4
+ config: DevOnboardingAdapterConfig;
5
+ catalogApiProducts: Record<string, CatalogApiProduct>;
6
+ userClaims: UserClaims;
7
+ rbacConfig: RbacConfig;
8
+ idpAccessToken?: string;
9
+ };
3
10
  export declare class AdapterFactory {
4
- getAdapter(config: DevOnboardingAdapterConfig, catalogApiProducts: string[], userClaims: UserClaims, idpAccessToken?: string): Adapter;
11
+ getAdapter(input: AdapterInput): Adapter;
5
12
  }
6
13
  //# sourceMappingURL=adapter-factory.d.ts.map
@@ -1 +1 @@
1
- import{ApigeeAdapter as E}from"./adapters/apigee/adapter.js";import{GraviteeAdapter as a}from"./adapters/gravitee/client.js";class m{getAdapter(e,r,t,p){switch(e.type){case"APIGEE_X":case"APIGEE_EDGE":return new E(e,t,r);case"GRAVITEE":return new a(e,t,p,r)}}}export{m as AdapterFactory};
1
+ import{ApigeeAdapter as r}from"./adapters/apigee/adapter.js";import{GraviteeAdapter as a}from"./adapters/gravitee/client.js";class A{getAdapter(e){switch(e.config.type){case"APIGEE_X":case"APIGEE_EDGE":return new r(e.config,e.rbacConfig,e.userClaims,e.catalogApiProducts);case"GRAVITEE":return new a(e.config,e.userClaims,e.idpAccessToken,e.catalogApiProducts)}}}export{A as AdapterFactory};
@@ -1,5 +1,5 @@
1
- import type { App, Paginated, Credential, ApiProduct, ExpandedApp, CredentialInput, UserClaims, Adapter, LogsParams, Log, ApiAccessStatus, LogDetails } from '../../types';
2
- import type { ApigeeAdapterConfig } from '@redocly/config';
1
+ import type { App, Paginated, Credential, ApiProduct, ExpandedApp, CredentialInput, UserClaims, Adapter, LogsParams, Log, ApiAccessStatus, LogDetails, CatalogApiProduct } from '../../types';
2
+ import type { ApigeeAdapterConfig, RbacConfig } from '@redocly/config';
3
3
  import { APIGEE_VERSION } from './types.js';
4
4
  export declare class ApigeeAdapter implements Adapter {
5
5
  apiUrl: string;
@@ -14,11 +14,12 @@ export declare class ApigeeAdapter implements Adapter {
14
14
  };
15
15
  config: ApigeeAdapterConfig;
16
16
  ignoreApiProducts: Set<string>;
17
- catalogApiProducts: Set<string>;
17
+ catalogApiProducts: Map<string, CatalogApiProduct>;
18
18
  allowApiProductsOutsideCatalog: boolean;
19
19
  adapterId: string;
20
20
  stage: string;
21
- constructor(config: ApigeeAdapterConfig, userClaims: UserClaims, catalogApiProducts: string[]);
21
+ rbacConfig: RbacConfig;
22
+ constructor(config: ApigeeAdapterConfig, rbacConfig: RbacConfig, userClaims: UserClaims, catalogApiProducts: Record<string, CatalogApiProduct>);
22
23
  setAuthHeader(init: any): Promise<any>;
23
24
  protected getErrorMessage(text: string): any;
24
25
  protected fetchData<T>(input: RequestInfo, init?: RequestInit): Promise<T>;
@@ -1,5 +1,5 @@
1
- import{logger as n}from"../../../../../tools/notifiers/logger.js";import{ApigeeDevOnboardingIntegrationAuthType as w,APIGEE_VERSION as m}from"./types.js";import{AlgorithmTypes as S}from"../../../../../web-server/jwt/types.js";import*as T from"../../../../../web-server/jwt/jwt.js";import{getFirstNameFromClaims as b,getLastNameFromClaims as U}from"../../../utils.js";import{HttpError as $}from"../../../../../utils/errors.js";const f="developer.service.DeveloperDoesNotExist",E=3600*24*365*1;function k(s){return s.replace(/\+/g,"%2B")}function y(s){return{id:s.consumerKey,clientId:s.consumerKey,clientSecret:s.consumerSecret,scopes:s.scopes,issuedAt:s.issuedAt,expiresAt:s.expiresAt,attributes:x(s.attributes),status:s.status,apiProductsStatus:s.apiProducts?.map(e=>({id:e.apiproduct,status:e.status,name:e.apiproduct}))||[],canBeRolled:!1,canBeRevoked:!0,valueToUseInHeader:"clientId"}}function A(s){const e=Object.fromEntries((s.attributes||[]).map(t=>[t.name,t.value]));return{id:s.name,name:e.DisplayName||s.name,description:e.Notes||"",attributes:e,createdAt:s.createdAt,lastModifiedAt:s.lastModifiedAt,scopes:s.scopes,credentials:s.credentials?.map(y)||[],supportsLogs:!1,canCreateKey:!0}}function O(s){const e=Object.fromEntries((s.attributes||[]).map(t=>[t.name,t.value]));return{id:s.name,name:e.DisplayName||s.name,description:s.description,attributes:e,createdAt:s.createdAt,lastModifiedAt:s.lastModifiedAt,approvalType:s.approvalType,scopes:s.scopes}}function v(s){return Object.entries(s||{}).map(([e,t])=>({name:e,value:t}))}function x(s){return Object.fromEntries((s||[]).map(e=>[e.name,e.value]))}let h=null;async function N(s){h?(await h).expiresAt<Date.now()/1e3&&(n.info("Token expired, requesting new apigee access token"),h=P(s)):(n.info("Requesting apigee access token"),h=P(s));try{return(await h).token}catch(e){throw h=null,e}}async function P({auth:s}){switch(s.type){case w.OAUTH2:return I(s);case w.SERVICE_ACCOUNT:return _(s)}}async function I(s){const e={grant_type:"client_credentials",client_id:s.clientId,client_secret:s.clientSecret};let t=await fetch(s.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(e).toString()});if(!t.ok)throw new Error(`Failed to get access token: ${t.statusText}
2
- ${await t.text()}`);const i=await t.json(),{access_token:a,expires_in:r}=i;return n.info("Apigee access token received"),{token:a,expiresAt:Math.floor(Date.now()/1e3)+r}}async function _(s){n.verbose(`Using service account to sign JWT token: Email: "%s" Private key:
1
+ import{logger as n}from"../../../../../tools/notifiers/logger.js";import{canAccessResource as S}from"../../../../../utils/rbac.js";import{ApigeeDevOnboardingIntegrationAuthType as w,APIGEE_VERSION as m}from"./types.js";import{AlgorithmTypes as b}from"../../../../../web-server/jwt/types.js";import*as T from"../../../../../web-server/jwt/jwt.js";import{getFirstNameFromClaims as U,getLastNameFromClaims as k}from"../../../utils.js";import{HttpError as $}from"../../../../../utils/errors.js";const f="developer.service.DeveloperDoesNotExist",C=3600*24*365*1;function O(s){return s.replace(/\+/g,"%2B")}function y(s){return{id:s.consumerKey,clientId:s.consumerKey,clientSecret:s.consumerSecret,scopes:s.scopes,issuedAt:s.issuedAt,expiresAt:s.expiresAt,attributes:N(s.attributes),status:s.status,apiProductsStatus:s.apiProducts?.map(e=>({id:e.apiproduct,status:e.status,name:e.apiproduct}))||[],canBeRolled:!1,canBeRevoked:!0,valueToUseInHeader:"clientId"}}function A(s){const e=Object.fromEntries((s.attributes||[]).map(t=>[t.name,t.value]));return{id:s.name,name:e.DisplayName||s.name,description:e.Notes||"",attributes:e,createdAt:s.createdAt,lastModifiedAt:s.lastModifiedAt,scopes:s.scopes,credentials:s.credentials?.map(y)||[],supportsLogs:!1,canCreateKey:!0}}function x(s){const e=Object.fromEntries((s.attributes||[]).map(t=>[t.name,t.value]));return{id:s.name,name:e.DisplayName||s.name,description:s.description,attributes:e,createdAt:s.createdAt,lastModifiedAt:s.lastModifiedAt,approvalType:s.approvalType,scopes:s.scopes}}function v(s){return Object.entries(s||{}).map(([e,t])=>({name:e,value:t}))}function N(s){return Object.fromEntries((s||[]).map(e=>[e.name,e.value]))}let h=null;async function I(s){h?(await h).expiresAt<Date.now()/1e3&&(n.info("Token expired, requesting new apigee access token"),h=P(s)):(n.info("Requesting apigee access token"),h=P(s));try{return(await h).token}catch(e){throw h=null,e}}async function P({auth:s}){switch(s.type){case w.OAUTH2:return j(s);case w.SERVICE_ACCOUNT:return _(s)}}async function j(s){const e={grant_type:"client_credentials",client_id:s.clientId,client_secret:s.clientSecret};let t=await fetch(s.tokenEndpoint,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(e).toString()});if(!t.ok)throw new Error(`Failed to get access token: ${t.statusText}
2
+ ${await t.text()}`);const a=await t.json(),{access_token:i,expires_in:r}=a;return n.info("Apigee access token received"),{token:i,expiresAt:Math.floor(Date.now()/1e3)+r}}async function _(s){n.verbose(`Using service account to sign JWT token: Email: "%s" Private key:
3
3
  %s
4
- `,s.serviceAccountEmail,s.serviceAccountPrivateKey.slice(0,30)+"***"+s.serviceAccountPrivateKey.slice(-30));const t={grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:await T.sign({sub:s.serviceAccountEmail,iss:s.serviceAccountEmail,aud:"https://www.googleapis.com/oauth2/v4/token",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+3600,scope:"https://www.googleapis.com/auth/cloud-platform"},s.serviceAccountPrivateKey,S.RS256)};n.verbose("Exchanging JWT for access token",s.serviceAccountEmail,s.serviceAccountPrivateKey.slice(0,30)+"***"+s.serviceAccountPrivateKey.slice(-30));let i=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(t).toString()});if(!i.ok)throw new Error(`Failed to get access token: ${i.statusText}
5
- ${await i.text()}`);const a=await i.json(),{access_token:r,expires_in:o}=a;return n.info("Apigee access token received"),{token:r,expiresAt:Math.floor(Date.now()/1e3)+o}}class L{apiUrl;developerUrl;accessToken=null;organizationName;email;organizationUrl;version;userClaims;config;ignoreApiProducts;catalogApiProducts;allowApiProductsOutsideCatalog;adapterId;stage;constructor(e,t,i){const a=e;this.apiUrl=a.apiUrl||"https://apigee.googleapis.com/v1",this.organizationName=a.organizationName,this.email=t.email,this.organizationUrl=`${this.apiUrl}/organizations/${this.organizationName}`,this.developerUrl=`${this.organizationUrl}/developers/${k(this.email)}`,this.version=e.type==="APIGEE_X"?m.x:m.edge,this.userClaims=t,this.adapterId=a.organizationName,this.ignoreApiProducts=new Set(a.ignoreApiProducts||[]),this.catalogApiProducts=new Set(i),this.allowApiProductsOutsideCatalog=a.allowApiProductsOutsideCatalog??!1,this.config=a,this.stage=a.stage||"non-production"}async setAuthHeader(e){return e=e||{},e.headers={...e.headers,"Accept-Encoding":"identity",accept:"application/json",Authorization:"Bearer "+await N(this.config),"X-Api-Key":this.config.auth.type===w.OAUTH2?this.config.auth.clientId:void 0},e}getErrorMessage(e){if(!e)return"Fetch error";try{const t=JSON.parse(e);return t.code===f?t.code:t.error.details[0]?.["@type"]==="type.googleapis.com/google.rpc.PreconditionFailure"&&t.error.details[0]?.violations[0]?.type===f?f:t?.message||t.error.message}catch{return e}}async fetchData(e,t){const i=await this.setAuthHeader(t),a=typeof e=="string"?e:e.url,r=typeof e=="string"?"GET":e.method;n.verbose(`${r.toUpperCase()} request to "${a}"`);const o=await fetch(e,i);if(!o.ok){const u=await o.text(),c=this.getErrorMessage(u);if(c===f){const p=this.userClaims,g=b(p),l=U(p),d=this.email;return await this.createDeveloper(this.email,g,l,d),await this.fetchData(e,t)}throw o.status!==404&&n.warn(`${r.toUpperCase()} request to "${a}" failed with ${o.status} code`),new $(o.status,c)}return o.headers.get("x-redocly-apigee-version")===m.x&&(this.version=m.x),n.verbose(`${r.toUpperCase()} request to "${a}" OK`),o.json()}async getApps(){const e=`${this.developerUrl}/apps?expand=true`,{app:t}=await this.fetchData(e),i=(t||[]).map(A);return{data:i,total:i.length}}async getApiProducts(){const e=`${this.organizationUrl}/apiproducts?expand=true`,{apiProduct:t}=await this.fetchData(e),i=(t||[]).filter(a=>(this.allowApiProductsOutsideCatalog||this.catalogApiProducts.has(a.name))&&!this.ignoreApiProducts.has(a.name)).map(O);return{data:i,total:i.length}}async getApp(e){const t=`${this.developerUrl}/apps/${e}${this.version===m.edge?"?expand=true":""}`,i=await this.fetchData(t);return A(i)}createDeveloper(e,t,i,a){const r=`${this.organizationUrl}/developers`;return this.fetchData(r,{headers:{"Content-type":"application/json"},body:JSON.stringify({email:e,firstName:t,lastName:i,userName:a}),method:"POST"})}async createApp(e){const t=`${this.developerUrl}/apps`;n.verbose(`Creating app ${e.id} ${e.apiProductIds}`);const i=await this.fetchData(t,{headers:{"Content-type":"application/json"},body:JSON.stringify({name:e.id,apiProducts:e.apiProductIds||[],keyExpiresIn:1*365*24*60*60*1e3,attributes:v({...e.attributes,DisplayName:e.name,Notes:e.description||""})}),method:"POST"});if(e.credentials){const a=e.credentials[0];await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/create`,{headers:{"Content-type":"application/json"},body:JSON.stringify({consumerKey:a.clientId,consumerSecret:a.clientSecret,expiresInSeconds:C(a.expiresAt,E)}),method:"POST"}),n.info("Imported previously provided key");const r=i.credentials[0].consumerKey;await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/${r}`,{method:"DELETE"}),n.info("Deleted autogenerated key");const o=await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/${a.clientId}`,{headers:{"Content-type":"application/json"},body:JSON.stringify({apiProducts:e.apiProductIds||[]}),method:"POST"});i.credentials=[o]}return A(i)}async updateApp(e,t){const i=`${this.developerUrl}/apps/${e}`;try{const a=await this.getApp(e),r={...t.attributes};t.name&&(r.DisplayName=t.name),t.description&&(r.Notes=t.description);const o=await this.fetchData(i,{headers:{"Content-type":"application/json"},body:JSON.stringify({attributes:v({...a.attributes,...r})}),method:"PUT"});return A(o)}catch(a){if(a.message.includes("does not exist"))return;throw a}}async deleteApp(e){const t=`${this.developerUrl}/apps/${e}`;try{return await this.fetchData(t,{headers:{"Content-type":"application/json"},method:"DELETE"})}catch(i){if(i.message.includes("does not exist"))return;throw i}}async createCredential(e,t){const i=`${this.developerUrl}/apps/${e}`;let a;if(t.clientId&&t.clientSecret)await this.fetchData(`${this.developerUrl}/apps/${e}/keys/create`,{headers:{"Content-type":"application/json"},body:JSON.stringify({consumerKey:t.clientId,consumerSecret:t.clientSecret,expiresInSeconds:C(t.expiresAt,E)}),method:"POST"}),a=await this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t.clientId}`,{headers:{"Content-type":"application/json"},body:JSON.stringify({apiProducts:t.apiProductIds||[]}),method:"POST"}),n.info("Imported previously provided key");else{const r=await this.getApp(e);a=(await this.fetchData(i,{headers:{"Content-type":"application/json"},method:"PUT",body:JSON.stringify({apiProducts:t.apiProductIds,attributes:v({...r.attributes,...t.attributes})})})).credentials[0]}return y(a)}async updateCredential(e,t,i){let a;try{a=await this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t}`)}catch{a=void 0}if(!a)throw new $(404,"Cannot update credential that does not exist");const r=`${this.developerUrl}/apps/${e}/keys/${t}`,o=await this.fetchData(r,{headers:{"Content-type":"application/json"},method:"PUT",body:JSON.stringify({apiProducts:i.apiProductIds})}),u=o.apiProducts.filter(c=>!i.apiProductIds.includes(c.apiproduct)).map(c=>c.apiproduct);return await Promise.all(u.map(c=>this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t}/apiproducts/${c}`,{method:"DELETE"}))),y(o)}async deleteCredential(e,t){const i=`${this.developerUrl}/apps/${e}/keys/${t}`;return y(await this.fetchData(i,{headers:{"Content-type":"application/octet-stream"},method:"DELETE"}))}async getAppLogDetails(e,t){throw new Error("Not implemented")}async getAppLogs(e,t){return{total:0,data:[]}}async getApiAccessStatus(e,t){const{data:i}=await this.getApps(),a=(i||[]).filter(p=>(p.credentials||[]).find(l=>l.apiProductsStatus.find(d=>d.id===e))),r=p=>a.find(g=>g.credentials?.find(d=>d.status===p&&d.apiProductsStatus.find(D=>D.id===e))),o=t?a.map(p=>{if(p.credentials?.find(l=>l.status==="approved"&&l.apiProductsStatus.find(d=>d.id===e)))return{id:p.id,title:p.name}}).filter(Boolean):void 0,u=r("approved");if(u)return{status:"ACCEPTED",appId:u.id,apps:o};const c=r("pending");return c?{status:"PENDING",appId:c.id,apps:o}:{status:"NONE"}}}function C(s,e){if(!(!s&&!e))return s=s||(Date.now()+e*1e3).toString(),Math.floor((parseInt(s,10)-Date.now())/1e3).toString()}export{L as ApigeeAdapter};
4
+ `,s.serviceAccountEmail,s.serviceAccountPrivateKey.slice(0,30)+"***"+s.serviceAccountPrivateKey.slice(-30));const t={grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:await T.sign({sub:s.serviceAccountEmail,iss:s.serviceAccountEmail,aud:"https://www.googleapis.com/oauth2/v4/token",iat:Math.floor(Date.now()/1e3),exp:Math.floor(Date.now()/1e3)+3600,scope:"https://www.googleapis.com/auth/cloud-platform"},s.serviceAccountPrivateKey,b.RS256)};n.verbose("Exchanging JWT for access token",s.serviceAccountEmail,s.serviceAccountPrivateKey.slice(0,30)+"***"+s.serviceAccountPrivateKey.slice(-30));let a=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(t).toString()});if(!a.ok)throw new Error(`Failed to get access token: ${a.statusText}
5
+ ${await a.text()}`);const i=await a.json(),{access_token:r,expires_in:o}=i;return n.info("Apigee access token received"),{token:r,expiresAt:Math.floor(Date.now()/1e3)+o}}class F{apiUrl;developerUrl;accessToken=null;organizationName;email;organizationUrl;version;userClaims;config;ignoreApiProducts;catalogApiProducts;allowApiProductsOutsideCatalog;adapterId;stage;rbacConfig;constructor(e,t,a,i){const r=e;this.apiUrl=r.apiUrl||"https://apigee.googleapis.com/v1",this.organizationName=r.organizationName,this.email=a.email,this.organizationUrl=`${this.apiUrl}/organizations/${this.organizationName}`,this.developerUrl=`${this.organizationUrl}/developers/${O(this.email)}`,this.version=e.type==="APIGEE_X"?m.x:m.edge,this.userClaims=a,this.adapterId=r.organizationName,this.ignoreApiProducts=new Set(r.ignoreApiProducts||[]),this.catalogApiProducts=new Map(Object.entries(i)),this.allowApiProductsOutsideCatalog=r.allowApiProductsOutsideCatalog??!1,this.config=r,this.stage=r.stage||"non-production",this.rbacConfig=t}async setAuthHeader(e){return e=e||{},e.headers={...e.headers,"Accept-Encoding":"identity",accept:"application/json",Authorization:"Bearer "+await I(this.config),"X-Api-Key":this.config.auth.type===w.OAUTH2?this.config.auth.clientId:void 0},e}getErrorMessage(e){if(!e)return"Fetch error";try{const t=JSON.parse(e);return t.code===f?t.code:t.error.details[0]?.["@type"]==="type.googleapis.com/google.rpc.PreconditionFailure"&&t.error.details[0]?.violations[0]?.type===f?f:t?.message||t.error.message}catch{return e}}async fetchData(e,t){const a=await this.setAuthHeader(t),i=typeof e=="string"?e:e.url,r=typeof e=="string"?"GET":e.method;n.verbose(`${r.toUpperCase()} request to "${i}"`);const o=await fetch(e,a);if(!o.ok){const u=await o.text(),c=this.getErrorMessage(u);if(c===f){const p=this.userClaims,g=U(p),l=k(p),d=this.email;return await this.createDeveloper(this.email,g,l,d),await this.fetchData(e,t)}throw o.status!==404&&n.warn(`${r.toUpperCase()} request to "${i}" failed with ${o.status} code`),new $(o.status,c)}return o.headers.get("x-redocly-apigee-version")===m.x&&(this.version=m.x),n.verbose(`${r.toUpperCase()} request to "${i}" OK`),o.json()}async getApps(){const e=`${this.developerUrl}/apps?expand=true`,{app:t}=await this.fetchData(e),a=(t||[]).map(A);return{data:a,total:a.length}}async getApiProducts(){const e=`${this.organizationUrl}/apiproducts?expand=true`,{apiProduct:t}=await this.fetchData(e),a=(t||[]).filter(i=>S(this.catalogApiProducts.get(i.name)||{},this.userClaims,this.rbacConfig)&&(this.allowApiProductsOutsideCatalog||this.catalogApiProducts.has(i.name))&&!this.ignoreApiProducts.has(i.name)).map(x);return{data:a,total:a.length}}async getApp(e){const t=`${this.developerUrl}/apps/${e}${this.version===m.edge?"?expand=true":""}`,a=await this.fetchData(t);return A(a)}createDeveloper(e,t,a,i){const r=`${this.organizationUrl}/developers`;return this.fetchData(r,{headers:{"Content-type":"application/json"},body:JSON.stringify({email:e,firstName:t,lastName:a,userName:i}),method:"POST"})}async createApp(e){const t=`${this.developerUrl}/apps`;n.verbose(`Creating app ${e.id} ${e.apiProductIds}`);const a=await this.fetchData(t,{headers:{"Content-type":"application/json"},body:JSON.stringify({name:e.id,apiProducts:e.apiProductIds||[],keyExpiresIn:1*365*24*60*60*1e3,attributes:v({...e.attributes,DisplayName:e.name,Notes:e.description||""})}),method:"POST"});if(e.credentials){const i=e.credentials[0];await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/create`,{headers:{"Content-type":"application/json"},body:JSON.stringify({consumerKey:i.clientId,consumerSecret:i.clientSecret,expiresInSeconds:E(i.expiresAt,C)}),method:"POST"}),n.info("Imported previously provided key");const r=a.credentials[0].consumerKey;await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/${r}`,{method:"DELETE"}),n.info("Deleted autogenerated key");const o=await this.fetchData(`${this.developerUrl}/apps/${e.id}/keys/${i.clientId}`,{headers:{"Content-type":"application/json"},body:JSON.stringify({apiProducts:e.apiProductIds||[]}),method:"POST"});a.credentials=[o]}return A(a)}async updateApp(e,t){const a=`${this.developerUrl}/apps/${e}`;try{const i=await this.getApp(e),r={...t.attributes};t.name&&(r.DisplayName=t.name),t.description&&(r.Notes=t.description);const o=await this.fetchData(a,{headers:{"Content-type":"application/json"},body:JSON.stringify({attributes:v({...i.attributes,...r})}),method:"PUT"});return A(o)}catch(i){if(i.message.includes("does not exist"))return;throw i}}async deleteApp(e){const t=`${this.developerUrl}/apps/${e}`;try{return await this.fetchData(t,{headers:{"Content-type":"application/json"},method:"DELETE"})}catch(a){if(a.message.includes("does not exist"))return;throw a}}async createCredential(e,t){const a=`${this.developerUrl}/apps/${e}`;let i;if(t.clientId&&t.clientSecret)await this.fetchData(`${this.developerUrl}/apps/${e}/keys/create`,{headers:{"Content-type":"application/json"},body:JSON.stringify({consumerKey:t.clientId,consumerSecret:t.clientSecret,expiresInSeconds:E(t.expiresAt,C)}),method:"POST"}),i=await this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t.clientId}`,{headers:{"Content-type":"application/json"},body:JSON.stringify({apiProducts:t.apiProductIds||[]}),method:"POST"}),n.info("Imported previously provided key");else{const r=await this.getApp(e);i=(await this.fetchData(a,{headers:{"Content-type":"application/json"},method:"PUT",body:JSON.stringify({apiProducts:t.apiProductIds,attributes:v({...r.attributes,...t.attributes})})})).credentials[0]}return y(i)}async updateCredential(e,t,a){let i;try{i=await this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t}`)}catch{i=void 0}if(!i)throw new $(404,"Cannot update credential that does not exist");const r=`${this.developerUrl}/apps/${e}/keys/${t}`,o=await this.fetchData(r,{headers:{"Content-type":"application/json"},method:"PUT",body:JSON.stringify({apiProducts:a.apiProductIds})}),u=o.apiProducts.filter(c=>!a.apiProductIds.includes(c.apiproduct)).map(c=>c.apiproduct);return await Promise.all(u.map(c=>this.fetchData(`${this.developerUrl}/apps/${e}/keys/${t}/apiproducts/${c}`,{method:"DELETE"}))),y(o)}async deleteCredential(e,t){const a=`${this.developerUrl}/apps/${e}/keys/${t}`;return y(await this.fetchData(a,{headers:{"Content-type":"application/octet-stream"},method:"DELETE"}))}async getAppLogDetails(e,t){throw new Error("Not implemented")}async getAppLogs(e,t){return{total:0,data:[]}}async getApiAccessStatus(e,t){const{data:a}=await this.getApps(),i=(a||[]).filter(p=>(p.credentials||[]).find(l=>l.apiProductsStatus.find(d=>d.id===e))),r=p=>i.find(g=>g.credentials?.find(d=>d.status===p&&d.apiProductsStatus.find(D=>D.id===e))),o=t?i.map(p=>{if(p.credentials?.find(l=>l.status==="approved"&&l.apiProductsStatus.find(d=>d.id===e)))return{id:p.id,title:p.name}}).filter(Boolean):void 0,u=r("approved");if(u)return{status:"ACCEPTED",appId:u.id,apps:o};const c=r("pending");return c?{status:"PENDING",appId:c.id,apps:o}:{status:"NONE"}}}function E(s,e){if(!(!s&&!e))return s=s||(Date.now()+e*1e3).toString(),Math.floor((parseInt(s,10)-Date.now())/1e3).toString()}export{F as ApigeeAdapter};
@@ -1,4 +1,4 @@
1
- import type { Adapter, App, Paginated, Credential, ApiProduct, ExpandedApp, LogsParams, Log, CredentialInput, ApiAccessStatus, LogDetails } from '../../types';
1
+ import type { Adapter, App, Paginated, Credential, ApiProduct, ExpandedApp, LogsParams, Log, CredentialInput, ApiAccessStatus, LogDetails, CatalogApiProduct } from '../../types';
2
2
  import type { GraviteeApp } from './types';
3
3
  import type { GraviteeAdapterConfig } from '@redocly/config';
4
4
  export declare class GraviteeAdapter implements Adapter {
@@ -9,13 +9,13 @@ export declare class GraviteeAdapter implements Adapter {
9
9
  email: string;
10
10
  };
11
11
  config: GraviteeAdapterConfig;
12
- catalogApiProducts: Set<string>;
12
+ catalogApiProducts: Map<string, CatalogApiProduct>;
13
13
  allowApiProductsOutsideCatalog: boolean;
14
14
  adapterId: string;
15
15
  stage: string;
16
16
  constructor(config: GraviteeAdapterConfig, userClaims: Record<string, string> & {
17
17
  email: string;
18
- }, accessToken: string, catalogApiProducts: string[]);
18
+ }, accessToken: string, catalogApiProducts: Record<string, CatalogApiProduct>);
19
19
  setAuthHeader(init: any): Promise<any>;
20
20
  protected getErrorMessage(text: string): any;
21
21
  protected fetchData<T>(input: RequestInfo, init?: RequestInit): Promise<T>;
@@ -1,2 +1,2 @@
1
1
  import{LRUCache as w}from"lru-cache";import{randomUUID as m}from"../../../../../utils/crypto/random-uuid.js";import{HTTP_METHOD_TO_QUERY_CODE as y}from"./constants.js";const h="developer.service.DeveloperDoesNotExist";function g(r,t){return{id:r.id,name:r.name,description:r.description,attributes:{},createdAt:Date.parse(r.created_at).toString(),lastModifiedAt:Date.parse(r.updated_at).toString(),scopes:[],credentials:t||[],clientId:r.settings?.app?.client_id||r.settings?.oauth?.client_id||void 0,apiProductIds:t?.flatMap(e=>e.apiProductIds||[])||[],canCreateKey:!t?.length,supportsLogs:!0}}function E({id:r,method:t,path:e,responseTime:s,status:a,timestamp:i,api:n}){return{id:r,timestamp:i.toString(),path:e,method:t,statusCode:a,responseTime:s,apiId:n}}function P({id:r,timestamp:t,metadata:e,status:s,requestContentLength:a,responseContentLength:i,request:n,response:c,path:d}){const o=Object.values(e).find(p=>p.ip)?.ip;return{id:r,timestamp:t.toString(),path:d,ipAddress:o,statusCode:s,request:{contentLength:a,body:n?.body,headers:n?.headers||{}},response:{contentLength:i,body:c?.body,headers:c?.headers||{}}}}function D(r){if(!r.apis?.length&&!r.methods?.length&&!r.statusCodes?.length)return;const t=[];return r.apis?.length&&t.push(`api:(${r.apis.join(" OR ")})`),r.methods?.length&&t.push(`method:(${r.methods.map(e=>y[e]).join(" OR ")})`),r.statusCodes?.length&&t.push(`status:(${r.statusCodes.join(" OR ")})`),t.join(" AND ")}function b({page:r,size:t,fromDate:e,toDate:s,order:a,filters:i}){return{page:r,size:t||50,from:e||Date.now()-1e3*60*60*24*90,to:s||Date.now(),order:a||"DESC",query:D(i)}}function l(r){switch(r){case"ACCEPTED":return"approved";case"REJECTED":case"PAUSED":return"revoked";case"PENDING":case"CLOSED":return"pending";default:throw new Error(`Unknown status: ${r}`)}}function S(r){return{id:r.id,name:r.name,description:r.description,attributes:{},createdAt:r.created_at?Date.parse(r.created_at).toString():"",lastModifiedAt:r.updated_at?Date.parse(r.updated_at).toString():"",scopes:[]}}const f=new w({max:1e4});async function $(r,t){console.log("Exchanging IdP token for new Gravitee token");let e=await fetch(`${r}/auth/oauth2/gravitee-am-oidc/_exchange?token=`+t,{method:"POST",headers:{Accept:"application/json"}});if(!e.ok)throw new Error(`Failed to get access token: ${e.statusText}
2
- ${await e.text()}`);console.log("Exchanged successfully");const s=await e.json(),{token:a,expires_in:i=3600*48}=s;return{token:a,expiresAt:Math.floor(Date.now()/1e3)+i}}async function T(r,t,e){if(!r.auth)return;if("static"in r.auth)return r.auth?.static;if(!e)throw new Error("No access token provided");let s=f.get(e);return(!s||(await s).expiresAt<Date.now()/1e3)&&(s=$(t,e).catch(a=>{throw f.delete(e),a}),f.set(e,s)),(await s).token}class U{apiUrl;accessToken;email;userClaims;config;catalogApiProducts;allowApiProductsOutsideCatalog;adapterId="";stage="";constructor(t,e,s,a){this.config=t,this.apiUrl=`${this.config.apiBaseUrl}/portal/environments/${this.config.env||"DEFAULT"}`,this.email=e.email,this.userClaims=e,this.accessToken=s,this.stage=t.stage||"non-production",this.catalogApiProducts=new Set(a),this.allowApiProductsOutsideCatalog=t.allowApiProductsOutsideCatalog??!1}async setAuthHeader(t){return t=t||{},t.headers={...t.headers,"Accept-Encoding":"identity",accept:"application/json",Authorization:"Bearer "+await T(this.config,this.apiUrl,this.accessToken)},t}getErrorMessage(t){if(!t)return"Fetch error";try{const e=JSON.parse(t);return e.code===h?e.code:e.error.details[0]?.["@type"]==="type.googleapis.com/google.rpc.PreconditionFailure"&&e.error.details[0]?.violations[0]?.type===h?h:e?.message||e.error.message}catch{return t}}async fetchData(t,e){const s=await this.setAuthHeader(e),a=await fetch(t,s);if(!a.ok){const i=await a.json();throw new Error(i.message||i?.errors?.[0]?.message)}return a.json()}async fetchAppCredentials(t,e=!1){const s=[],a=await this.fetchData(`${this.apiUrl}/subscriptions?applicationId=${t.id}&size=-1&statuses=ACCEPTED&statuses=PAUSED&statuses=PENDING`),i=a.data[0],n=t.api_key_mode==="SHARED"?i?[i]:[]:a.data;return await Promise.all(n.map(async c=>{const d=await this.fetchData(`${this.apiUrl}/subscriptions/${c.id}?include=keys`);d.keys?.length||(s.push({id:d.id,expiresAt:"-1",issuedAt:"-1",canBeRolled:!0,canBeRevoked:!1,clientSecret:"",attributes:{},status:l(i.status),apiProductsStatus:t.api_key_mode==="SHARED"?a.data.map(o=>({id:o.id,apiId:o.api,name:a.metadata[o.api].name,status:l(o.status)})):[{id:c.id,apiId:c.api,name:a.metadata[c.api].name,status:l(c.status)}]}),s[s.length-1].apiProductIds=s[s.length-1].apiProductsStatus.map(o=>o.apiId));for(const o of d.keys||[])o.expired||o.revoked&&!e||(s.push({id:o.key,expiresAt:o.expire_at?Date.parse(o.expire_at).toString():"-1",canBeRolled:o.expire_at==null,canBeRevoked:!1,issuedAt:Date.parse(o.created_at).toString(),clientSecret:o.key,attributes:{},status:l(i.status),apiProductsStatus:t.api_key_mode==="SHARED"?a.data.map(p=>({id:p.id,apiId:p.api,name:a.metadata[p.api].name,status:l(p.status)})):o.subscriptions.map(p=>({id:c.id,apiId:p.api,name:a.metadata[p.api].name,status:l(c.status)}))}),s[s.length-1].apiProductIds=s[s.length-1].apiProductsStatus.map(p=>p.apiId))})),s}async unsubscribeFromApis(t,e){const s=await this.getApp(t),a=[],i=await this.setAuthHeader({});return e.map(n=>{const c=s.credentials.find(u=>u.apiProductIds?.includes(n)),d=c?.apiProductIds?.findIndex(u=>u===n),o=c?.apiProductsStatus[d??-1]?.id;if(!o)throw new Error("Not found");const p=`${this.apiUrl}/subscriptions/${o}/_close`;a.push(fetch(p,{...i,method:"POST"}))}),Promise.all(a)}async subscribeToApis(t,e){return Promise.all(e.map(async s=>{const a=await this.fetchData(`${this.apiUrl}/apis/${s}/plans?size=-1`),i=a.data.find(n=>n.security==="API_KEY"&&n.validation=="AUTO")||a.data.find(n=>n.security==="API_KEY")||a.data[0];if(!i)throw new Error("No plan found");return this.fetchData(`${this.apiUrl}/subscriptions`,{headers:{"Content-type":"application/json"},body:JSON.stringify({application:t,plan:i.id,request:""}),method:"POST"})}))}async getApps(){const t=`${this.apiUrl}/applications?size=-1`,e=await this.fetchData(t),{data:s}=e,a=(s||[]).map(i=>g(i));return{data:a,total:a.length}}async getApiProducts(){const t=`${this.apiUrl}/apis?size=-1`,e=await this.fetchData(t),{data:s}=e,a=(s||[]).filter(i=>this.allowApiProductsOutsideCatalog||this.catalogApiProducts.has(i.name)).map(i=>S(i));return{data:a,total:a.length}}async getApp(t){const e=`${this.apiUrl}/applications/${t}`,s=await this.fetchData(e);let a=await this.fetchAppCredentials(s);return g(s,a)}async createApp(t){const e=`${this.apiUrl}/applications`;if(!t.apiProductIds||!t.apiProductIds.length)throw new Error("At least one API Product is required");const s=await this.fetchData(e,{headers:{"Content-type":"application/json"},body:JSON.stringify({name:t.name,description:t.description||"",domain:t.attributes?.domain||null,picture:t.attributes?.picture||null,api_key_mode:"SHARED",background:void 0,settings:{app:{type:t.attributes?.appType||"",client_id:t.attributes?.clientId||m()}}}),method:"POST"});if(await this.subscribeToApis(s.id,t.apiProductIds||[]),t.credentials)throw new Error("Gravitee adapter does not support manual credentials");return this.getApp(s.id)}async getAppLogDetails(t,e){const s=`${this.apiUrl}/applications/${t}/logs/${e}`,a=await this.fetchData(s);return P(a)}async getAppLogs(t,e){const s=new URL(`${this.apiUrl}/applications/${t}/logs`),a=b(e);Object.keys(a).forEach(n=>{a[n]&&s.searchParams.append(n,a[n])});const i=await this.fetchData(s.toString());return{total:i.metadata.data.total,data:i.data.map(E)}}async updateApp(t,e){const s=`${this.apiUrl}/applications/${t}`,a=await this.getApp(t),i=await this.fetchData(s,{headers:{"Content-type":"application/json"},body:JSON.stringify({id:t,name:e.name||a.name,description:e.description||a.description||"",domain:e.attributes?.domain||a.attributes?.domain||null,picture:e.attributes?.picture||a.attributes?.picture||null,api_key_mode:"SHARED",background:void 0,settings:{app:{type:e.attributes?.appType||a.attributes?.appType||"",client_id:e.attributes?.clientId||a.attributes?.clientId||m()}}}),method:"PUT"});return this.getApp(i.id)}async deleteApp(t){const e=`${this.apiUrl}/applications/${t}`;await this.fetchData(e,{method:"DELETE"})}async createCredential(t,e){const s=await this.getApp(t),a=new Set(e.apiProductIds.filter(c=>!(s.apiProductIds||[]).includes(c))),i=new Set((s.apiProductIds||[]).filter(c=>!e.apiProductIds.includes(c)));return await this.subscribeToApis(t,Array.from(a)),await this.unsubscribeFromApis(t,Array.from(i)),(await this.getApp(t)).credentials[0]}async updateCredential(t,e,s){if(s.apiProductIds)return this.createCredential(t,s);const a=`${this.apiUrl}/applications/${t}`,i=await this.fetchData(a);let n;if(i.api_key_mode==="SHARED")n=`${this.apiUrl}/applications/${t}/keys/_renew`;else{const p=(await this.fetchAppCredentials(i)).find(A=>A.id===e);if(!p)throw new Error("Not found!");const u=p.apiProductsStatus[0].id;n=`${this.apiUrl}/subscriptions/${u}/keys/_renew`}const c=await fetch(n,await this.setAuthHeader({method:"POST"}));if(!c.ok)throw new Error(`Failed to renew the key: ${c.statusText}`);return(await this.fetchAppCredentials(i,!0)).find(o=>o.id===e)}async deleteCredential(t,e){const s=`${this.apiUrl}/applications/${t}`,a=await this.fetchData(s);let i;if(a.api_key_mode==="SHARED")i=`${this.apiUrl}/applications/${t}/keys/${e}/_revoke`;else{const o=(await this.fetchAppCredentials(a)).find(u=>u.id===e);if(!o)throw new Error("Not found!");const p=o.apiProductsStatus[0].id;i=`${this.apiUrl}/subscriptions/${p}/keys/${e}/_revoke`}const n=await fetch(i,await this.setAuthHeader({method:"POST"}));if(!n.ok)throw new Error(`Failed to remove the key: ${n.statusText}`);return(await this.fetchAppCredentials(a,!0)).find(d=>d.id===e)}async getApiAccessStatus(t,e){const s=`${this.apiUrl}/subscriptions?apiId=${t}&size=-1&statuses=ACCEPTED&statuses=PENDING`,a=await this.fetchData(s),i=e?a.data.map(d=>{if(d.status==="ACCEPTED")return{id:d.application,title:a.metadata[d.application]?.name||d.application}}).filter(Boolean):void 0,n=a.data.find(d=>d.status==="ACCEPTED");if(n)return{status:"ACCEPTED",appId:n.application,apps:i};const c=a.data.find(d=>d.status==="PENDING");return c?{status:"PENDING",appId:c.application,apps:i}:{status:"NONE"}}}export{U as GraviteeAdapter};
2
+ ${await e.text()}`);console.log("Exchanged successfully");const s=await e.json(),{token:a,expires_in:i=3600*48}=s;return{token:a,expiresAt:Math.floor(Date.now()/1e3)+i}}async function T(r,t,e){if(!r.auth)return;if("static"in r.auth)return r.auth?.static;if(!e)throw new Error("No access token provided");let s=f.get(e);return(!s||(await s).expiresAt<Date.now()/1e3)&&(s=$(t,e).catch(a=>{throw f.delete(e),a}),f.set(e,s)),(await s).token}class U{apiUrl;accessToken;email;userClaims;config;catalogApiProducts;allowApiProductsOutsideCatalog;adapterId="";stage="";constructor(t,e,s,a){this.config=t,this.apiUrl=`${this.config.apiBaseUrl}/portal/environments/${this.config.env||"DEFAULT"}`,this.email=e.email,this.userClaims=e,this.accessToken=s,this.stage=t.stage||"non-production",this.catalogApiProducts=new Map(Object.entries(a)),this.allowApiProductsOutsideCatalog=t.allowApiProductsOutsideCatalog??!1}async setAuthHeader(t){return t=t||{},t.headers={...t.headers,"Accept-Encoding":"identity",accept:"application/json",Authorization:"Bearer "+await T(this.config,this.apiUrl,this.accessToken)},t}getErrorMessage(t){if(!t)return"Fetch error";try{const e=JSON.parse(t);return e.code===h?e.code:e.error.details[0]?.["@type"]==="type.googleapis.com/google.rpc.PreconditionFailure"&&e.error.details[0]?.violations[0]?.type===h?h:e?.message||e.error.message}catch{return t}}async fetchData(t,e){const s=await this.setAuthHeader(e),a=await fetch(t,s);if(!a.ok){const i=await a.json();throw new Error(i.message||i?.errors?.[0]?.message)}return a.json()}async fetchAppCredentials(t,e=!1){const s=[],a=await this.fetchData(`${this.apiUrl}/subscriptions?applicationId=${t.id}&size=-1&statuses=ACCEPTED&statuses=PAUSED&statuses=PENDING`),i=a.data[0],n=t.api_key_mode==="SHARED"?i?[i]:[]:a.data;return await Promise.all(n.map(async c=>{const d=await this.fetchData(`${this.apiUrl}/subscriptions/${c.id}?include=keys`);d.keys?.length||(s.push({id:d.id,expiresAt:"-1",issuedAt:"-1",canBeRolled:!0,canBeRevoked:!1,clientSecret:"",attributes:{},status:l(i.status),apiProductsStatus:t.api_key_mode==="SHARED"?a.data.map(o=>({id:o.id,apiId:o.api,name:a.metadata[o.api].name,status:l(o.status)})):[{id:c.id,apiId:c.api,name:a.metadata[c.api].name,status:l(c.status)}]}),s[s.length-1].apiProductIds=s[s.length-1].apiProductsStatus.map(o=>o.apiId));for(const o of d.keys||[])o.expired||o.revoked&&!e||(s.push({id:o.key,expiresAt:o.expire_at?Date.parse(o.expire_at).toString():"-1",canBeRolled:o.expire_at==null,canBeRevoked:!1,issuedAt:Date.parse(o.created_at).toString(),clientSecret:o.key,attributes:{},status:l(i.status),apiProductsStatus:t.api_key_mode==="SHARED"?a.data.map(p=>({id:p.id,apiId:p.api,name:a.metadata[p.api].name,status:l(p.status)})):o.subscriptions.map(p=>({id:c.id,apiId:p.api,name:a.metadata[p.api].name,status:l(c.status)}))}),s[s.length-1].apiProductIds=s[s.length-1].apiProductsStatus.map(p=>p.apiId))})),s}async unsubscribeFromApis(t,e){const s=await this.getApp(t),a=[],i=await this.setAuthHeader({});return e.map(n=>{const c=s.credentials.find(u=>u.apiProductIds?.includes(n)),d=c?.apiProductIds?.findIndex(u=>u===n),o=c?.apiProductsStatus[d??-1]?.id;if(!o)throw new Error("Not found");const p=`${this.apiUrl}/subscriptions/${o}/_close`;a.push(fetch(p,{...i,method:"POST"}))}),Promise.all(a)}async subscribeToApis(t,e){return Promise.all(e.map(async s=>{const a=await this.fetchData(`${this.apiUrl}/apis/${s}/plans?size=-1`),i=a.data.find(n=>n.security==="API_KEY"&&n.validation=="AUTO")||a.data.find(n=>n.security==="API_KEY")||a.data[0];if(!i)throw new Error("No plan found");return this.fetchData(`${this.apiUrl}/subscriptions`,{headers:{"Content-type":"application/json"},body:JSON.stringify({application:t,plan:i.id,request:""}),method:"POST"})}))}async getApps(){const t=`${this.apiUrl}/applications?size=-1`,e=await this.fetchData(t),{data:s}=e,a=(s||[]).map(i=>g(i));return{data:a,total:a.length}}async getApiProducts(){const t=`${this.apiUrl}/apis?size=-1`,e=await this.fetchData(t),{data:s}=e,a=(s||[]).filter(i=>this.allowApiProductsOutsideCatalog||this.catalogApiProducts.has(i.name)).map(i=>S(i));return{data:a,total:a.length}}async getApp(t){const e=`${this.apiUrl}/applications/${t}`,s=await this.fetchData(e);let a=await this.fetchAppCredentials(s);return g(s,a)}async createApp(t){const e=`${this.apiUrl}/applications`;if(!t.apiProductIds||!t.apiProductIds.length)throw new Error("At least one API Product is required");const s=await this.fetchData(e,{headers:{"Content-type":"application/json"},body:JSON.stringify({name:t.name,description:t.description||"",domain:t.attributes?.domain||null,picture:t.attributes?.picture||null,api_key_mode:"SHARED",background:void 0,settings:{app:{type:t.attributes?.appType||"",client_id:t.attributes?.clientId||m()}}}),method:"POST"});if(await this.subscribeToApis(s.id,t.apiProductIds||[]),t.credentials)throw new Error("Gravitee adapter does not support manual credentials");return this.getApp(s.id)}async getAppLogDetails(t,e){const s=`${this.apiUrl}/applications/${t}/logs/${e}`,a=await this.fetchData(s);return P(a)}async getAppLogs(t,e){const s=new URL(`${this.apiUrl}/applications/${t}/logs`),a=b(e);Object.keys(a).forEach(n=>{a[n]&&s.searchParams.append(n,a[n])});const i=await this.fetchData(s.toString());return{total:i.metadata.data.total,data:i.data.map(E)}}async updateApp(t,e){const s=`${this.apiUrl}/applications/${t}`,a=await this.getApp(t),i=await this.fetchData(s,{headers:{"Content-type":"application/json"},body:JSON.stringify({id:t,name:e.name||a.name,description:e.description||a.description||"",domain:e.attributes?.domain||a.attributes?.domain||null,picture:e.attributes?.picture||a.attributes?.picture||null,api_key_mode:"SHARED",background:void 0,settings:{app:{type:e.attributes?.appType||a.attributes?.appType||"",client_id:e.attributes?.clientId||a.attributes?.clientId||m()}}}),method:"PUT"});return this.getApp(i.id)}async deleteApp(t){const e=`${this.apiUrl}/applications/${t}`;await this.fetchData(e,{method:"DELETE"})}async createCredential(t,e){const s=await this.getApp(t),a=new Set(e.apiProductIds.filter(c=>!(s.apiProductIds||[]).includes(c))),i=new Set((s.apiProductIds||[]).filter(c=>!e.apiProductIds.includes(c)));return await this.subscribeToApis(t,Array.from(a)),await this.unsubscribeFromApis(t,Array.from(i)),(await this.getApp(t)).credentials[0]}async updateCredential(t,e,s){if(s.apiProductIds)return this.createCredential(t,s);const a=`${this.apiUrl}/applications/${t}`,i=await this.fetchData(a);let n;if(i.api_key_mode==="SHARED")n=`${this.apiUrl}/applications/${t}/keys/_renew`;else{const p=(await this.fetchAppCredentials(i)).find(A=>A.id===e);if(!p)throw new Error("Not found!");const u=p.apiProductsStatus[0].id;n=`${this.apiUrl}/subscriptions/${u}/keys/_renew`}const c=await fetch(n,await this.setAuthHeader({method:"POST"}));if(!c.ok)throw new Error(`Failed to renew the key: ${c.statusText}`);return(await this.fetchAppCredentials(i,!0)).find(o=>o.id===e)}async deleteCredential(t,e){const s=`${this.apiUrl}/applications/${t}`,a=await this.fetchData(s);let i;if(a.api_key_mode==="SHARED")i=`${this.apiUrl}/applications/${t}/keys/${e}/_revoke`;else{const o=(await this.fetchAppCredentials(a)).find(u=>u.id===e);if(!o)throw new Error("Not found!");const p=o.apiProductsStatus[0].id;i=`${this.apiUrl}/subscriptions/${p}/keys/${e}/_revoke`}const n=await fetch(i,await this.setAuthHeader({method:"POST"}));if(!n.ok)throw new Error(`Failed to remove the key: ${n.statusText}`);return(await this.fetchAppCredentials(a,!0)).find(d=>d.id===e)}async getApiAccessStatus(t,e){const s=`${this.apiUrl}/subscriptions?apiId=${t}&size=-1&statuses=ACCEPTED&statuses=PENDING`,a=await this.fetchData(s),i=e?a.data.map(d=>{if(d.status==="ACCEPTED")return{id:d.application,title:a.metadata[d.application]?.name||d.application}}).filter(Boolean):void 0,n=a.data.find(d=>d.status==="ACCEPTED");if(n)return{status:"ACCEPTED",appId:n.application,apps:i};const c=a.data.find(d=>d.status==="PENDING");return c?{status:"PENDING",appId:c.application,apps:i}:{status:"NONE"}}}export{U as GraviteeAdapter};
@@ -1,6 +1,7 @@
1
1
  import type { Context, Next } from 'hono';
2
- import type { DevOnboardingAdapterConfig } from '@redocly/config';
3
- export declare function integrationsMiddleware(integrations: DevOnboardingAdapterConfig[], catalogApiProducts: string[]): (ctx: Context, next: Next) => Promise<(Response & import("hono").TypedResponse<{
2
+ import type { DevOnboardingAdapterConfig, RbacConfig } from '@redocly/config';
3
+ import type { CatalogApiProduct } from '../types.js';
4
+ export declare function integrationsMiddleware(integrations: DevOnboardingAdapterConfig[], catalogApiProducts: Record<string, CatalogApiProduct>, rbacConfig: RbacConfig): (ctx: Context, next: Next) => Promise<(Response & import("hono").TypedResponse<{
4
5
  error: string;
5
6
  }, 401, "json">) | undefined>;
6
7
  //# sourceMappingURL=integrations.d.ts.map
@@ -1 +1 @@
1
- import{AdapterFactory as i}from"../adapter-factory.js";import{CombineAdapter as s}from"../combine.js";const p=new i;function A(n,o){return async(e,d)=>{const t=e.get("auth");if(!t.isAuthenticated)return e.json({error:"Unauthorized"},401);const r=n.map(a=>p.getAdapter(a,o,t.claims,t.idpAccessToken));if(r.length===1)e.set("devOnboardingAdapter",r[0]);else{const a=new s(r);e.set("devOnboardingAdapter",a)}await d()}}export{A as integrationsMiddleware};
1
+ import{AdapterFactory as s}from"../adapter-factory.js";import{CombineAdapter as p}from"../combine.js";const c=new s;function u(n,o,i){return async(e,d)=>{const t=e.get("auth");if(!t.isAuthenticated)return e.json({error:"Unauthorized"},401);const r=n.map(a=>c.getAdapter({config:a,catalogApiProducts:o,userClaims:t.claims,rbacConfig:i,idpAccessToken:t.idpAccessToken}));if(r.length===1)e.set("devOnboardingAdapter",r[0]);else{const a=new p(r);e.set("devOnboardingAdapter",a)}await d()}}export{u as integrationsMiddleware};
@@ -1 +1 @@
1
- import{requireAuthMiddleware as e}from"../middlewares/auth.js";import{integrationsMiddleware as s}from"../middlewares/integrations.js";import{listApps as d,createApp as l,getApp as g,createCredential as c,updateCredential as n,deleteCredential as k,deleteApp as y,updateApp as o,getLogs as A,getLogDetails as m}from"./apps.js";import{getApiAccessStatus as f,listApiProducts as u}from"./products.js";function P(i,t){const p=t.getConfig().developerOnboarding?.adapters||[],a=Object.keys(t.globalData.apiProducts||{});p&&(i.get("/api/api-keys/apps",e(),s(p,a),d(t)),i.post("/api/api-keys/apps",e(),s(p,a),l(t)),i.get("/api/api-keys/apps/:id",e(),s(p,a),g(t)),i.put("/api/api-keys/apps/:id",e(),s(p,a),o(t)),i.delete("/api/api-keys/apps/:id",e(),s(p,a),y(t)),i.post("/api/api-keys/apps/:id/credentials",e(),s(p,a),c(t)),i.put("/api/api-keys/apps/:id/credentials/:credentialId",e(),s(p,a),n(t)),i.delete("/api/api-keys/apps/:id/credentials/:credentialId",e(),s(p,a),k(t)),i.get("/api/api-keys/apps/:id/logs/:logId",e(),s(p,a),m()),i.get("/api/api-keys/apps/:id/logs",e(),s(p,a),A()),i.get("/api/api-keys/api-products",e(),s(p,a),u(t)),i.get("/api/api-keys/api-products/:id/access",e(),s(p,a),f()))}export{P as installRoutes};
1
+ import{requireAuthMiddleware as t}from"../middlewares/auth.js";import{integrationsMiddleware as d}from"../middlewares/integrations.js";import{listApps as l,createApp as c,getApp as g,createCredential as n,updateCredential as o,deleteCredential as k,deleteApp as y,updateApp as A,getLogs as f,getLogDetails as m}from"./apps.js";import{getApiAccessStatus as C,listApiProducts as b}from"./products.js";function v(a,i){const p=i.getConfig().developerOnboarding?.adapters||[],e=i.getConfig().access?.rbac||{},s=i.globalData.apiProducts||{};p&&(a.get("/api/api-keys/apps",t(),d(p,s,e),l(i)),a.post("/api/api-keys/apps",t(),d(p,s,e),c(i)),a.get("/api/api-keys/apps/:id",t(),d(p,s,e),g(i)),a.put("/api/api-keys/apps/:id",t(),d(p,s,e),A(i)),a.delete("/api/api-keys/apps/:id",t(),d(p,s,e),y(i)),a.post("/api/api-keys/apps/:id/credentials",t(),d(p,s,e),n(i)),a.put("/api/api-keys/apps/:id/credentials/:credentialId",t(),d(p,s,e),o(i)),a.delete("/api/api-keys/apps/:id/credentials/:credentialId",t(),d(p,s,e),k(i)),a.get("/api/api-keys/apps/:id/logs/:logId",t(),d(p,s,e),m()),a.get("/api/api-keys/apps/:id/logs",t(),d(p,s,e),f()),a.get("/api/api-keys/api-products",t(),d(p,s,e),b(i)),a.get("/api/api-keys/api-products/:id/access",t(),d(p,s,e),C()))}export{v as installRoutes};
@@ -1,3 +1,4 @@
1
+ import type { REDOCLY_ROUTE_RBAC, REDOCLY_TEAMS_RBAC, RbacScopeItems } from '@redocly/config';
1
2
  export type App = {
2
3
  id: string;
3
4
  name: string;
@@ -16,6 +17,13 @@ export type ExpandedApp = {
16
17
  credentials: Credential[];
17
18
  _adapterIds?: string[];
18
19
  } & App;
20
+ export type CatalogApiProduct = {
21
+ [REDOCLY_TEAMS_RBAC]?: RbacScopeItems;
22
+ [REDOCLY_ROUTE_RBAC]?: {
23
+ slug?: string;
24
+ fsPath?: string;
25
+ };
26
+ };
19
27
  export type ApiProduct = {
20
28
  id: string;
21
29
  name: string;
@@ -1,4 +1,4 @@
1
- import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-popup";import l from"styled-components";import{useQuery as N}from"@tanstack/react-query";import{DateTimeRangePicker as R}from"@wojtekmaj/react-datetimerange-picker";import"@wojtekmaj/react-datetimerange-picker/dist/DateTimeRangePicker.css";import{fetch as S}from"../../../../../client/app/utils";import{PopupMenuUl as I}from"./PopupMenuUl";const A=[{name:"OTHER",id:"OTHER"},{name:"CONNECT",id:"CONNECT"},{name:"DELETE",id:"DELETE"},{name:"GET",id:"GET"},{name:"HEAD",id:"HEAD"},{name:"OPTIONS",id:"OPTIONS"},{name:"PATCH",id:"PATCH"},{name:"POST",id:"POST"},{name:"PUT",id:"PUT"},{name:"TRACE",id:"TRACE"}],y=[{type:"predefined",name:"last 5m",fromDateOffset:1e3*60*5},{type:"predefined",name:"last 1h",fromDateOffset:1e3*60*60},{type:"predefined",name:"last 3h",fromDateOffset:1e3*60*60*3},{type:"predefined",name:"last 12h",fromDateOffset:1e3*60*60*12},{type:"predefined",name:"last 1d",fromDateOffset:1e3*60*60*24},{type:"predefined",name:"last 3d",fromDateOffset:1e3*60*60*24*3},{type:"predefined",name:"last 7d",fromDateOffset:1e3*60*60*24*7},{type:"predefined",name:"last 30d",fromDateOffset:1e3*60*60*24*30}],P=[{name:"100 CONTINUE",id:"100"},{name:"101 SWITCHING PROTOCOLS",id:"101"},{name:"102 PROCESSING",id:"102"},{name:"200 OK",id:"200"},{name:"201 CREATED",id:"201"},{name:"202 ACCEPTED",id:"202"},{name:"203 NON AUTHORITATIVE INFORMATION",id:"203"},{name:"204 NO CONTENT",id:"204"},{name:"205 RESET CONTENT",id:"205"},{name:"206 PARTIAL CONTENT",id:"206"},{name:"207 MULTI STATUS",id:"207"},{name:"300 MULTIPLE CHOICES",id:"300"},{name:"301 MOVED PERMANENTLY",id:"301"},{name:"302 FOUND",id:"302"},{name:"303 SEE OTHER",id:"303"},{name:"304 NOT MODIFIED",id:"304"},{name:"305 USE PROXY",id:"305"},{name:"307 TEMPORARY REDIRECT",id:"307"},{name:"400 BAD REQUEST",id:"400"},{name:"401 UNAUTHORIZED",id:"401"},{name:"402 PAYMENT REQUIRED",id:"402"},{name:"403 FORBIDDEN",id:"403"},{name:"404 NOT FOUND",id:"404"},{name:"405 METHOD NOT ALLOWED",id:"405"},{name:"406 NOT ACCEPTABLE",id:"406"},{name:"407 PROXY AUTHENTICATION REQUIRED",id:"407"},{name:"408 REQUEST TIMEOUT",id:"408"},{name:"409 CONFLICT",id:"409"},{name:"410 GONE",id:"410"},{name:"411 LENGTH REQUIRED",id:"411"},{name:"412 PRECONDITION FAILED",id:"412"},{name:"413 REQUEST ENTITY TOO LARGE",id:"413"},{name:"414 REQUEST URI TOO LONG",id:"414"},{name:"415 UNSUPPORTED MEDIA TYPE",id:"415"},{name:"416 REQUESTED RANGE NOT SATISFIABLE",id:"416"},{name:"417 EXPECTATION FAILED",id:"417"},{name:"422 UNPROCESSABLE ENTITY",id:"422"},{name:"423 LOCKED",id:"423"},{name:"424 FAILED DEPENDENCY",id:"424"},{name:"429 TOO MANY REQUESTS",id:"429"},{name:"500 INTERNAL SERVER ERROR",id:"500"},{name:"501 NOT IMPLEMENTED",id:"501"},{name:"502 BAD GATEWAY",id:"502"},{name:"503 SERVICE UNAVAILABLE",id:"503"},{name:"504 GATEWAY TIMEOUT",id:"504"},{name:"505 HTTP VERSION NOT SUPPORTED",id:"505"},{name:"507 INSUFFICIENT STORAGE",id:"507"}];function x(e){return n.createElement(L,null,n.createElement(w,{onFiltersChange:e.onFiltersChange,activeFilters:e.activeFilters}),n.createElement(U,null))}var j=n.memo(x);const U=()=>null,w=n.memo(e=>{const{onFiltersChange:d,activeFilters:t}=e,{data:i}=N({queryKey:["API-PRODUCTS"],queryFn:()=>S("/api/api-keys/api-products").then(o=>o.json())}),[s,r]=O(),a=()=>r(void 0),m=(o,g)=>{const T={...t,[o]:g};d(T)};return n.createElement(h,{trigger:n.createElement(k,null,"Filter"),position:"bottom right"},n.createElement(_,null,n.createElement(E,{open:s==="api",name:"API name",options:i||[],activeOptions:t.api,onOpen:()=>r("api"),onClose:a,onChange:o=>m("api",o),active:(t.api?.length??0)>0}),n.createElement(E,{open:s==="status",name:"Status",options:P,activeOptions:t.status,onOpen:()=>r("status"),onClose:a,onChange:o=>m("status",o),active:(t.status?.length??0)>0}),n.createElement(E,{open:s==="method",name:"Method",options:A,activeOptions:t.method,onOpen:()=>r("method"),onClose:a,onChange:o=>m("method",o),active:(t.method?.length??0)>0}),n.createElement(E,{name:"Date range",open:s==="date",onOpen:()=>r("date"),onClose:a,active:!!t.date,renderSubmenu:()=>n.createElement(M,{onChange:o=>m("date",o),selectedDateFilter:t.date})}),n.createElement(b,null),n.createElement(H,{onClick:()=>d({})},"Clear filters")))}),M=e=>{const{selectedDateFilter:d,onChange:t}=e,i=d?.type==="custom",[s,r]=O(!1),[a,m]=O([null,null]),o=a?.[0]?a[0]:null,g=a?.[1]?a[1]:null,T=()=>{if(!a?.[0]&&!a?.[1])return t();const c=a[1]||new Date,p=a[0]||new Date(c.getDate()-1e3*60*60*24);r(!1),t({type:"custom",fromDate:p.getTime(),toDate:c.getTime()})};return n.createElement(v,{onMouseDown:u,onTouchStart:u},n.createElement(E,{open:s,active:i,name:"Custom range",onOpen:()=>r(!0),onClose:()=>r(!1),renderSubmenu:()=>n.createElement(F,null,n.createElement(Y,{onClick:T},"Submit"),n.createElement(b,null),n.createElement(R,{isCalendarOpen:!0,format:"yyyy-MM-dd HH:mm",closeWidgets:!1,dayPlaceholder:"DD",monthPlaceholder:"MM",yearPlaceholder:"YYYY",hourPlaceholder:"HH",minutePlaceholder:"mm",disableClock:!0,value:[o,g],onChange:c=>m(c)}))}),y.map(c=>{const p=d?.type==="predefined"&&d.name===c.name;return n.createElement(f,{active:p,key:c.name,onClick:C=>{u(C),t(p?void 0:c)}},c.name)}))},E=e=>{const d=e.activeOptions||[],t=D(()=>new Set(d.map(i=>i.id)),[e.activeOptions]);return n.createElement(h,{open:e.open,onOpen:e.onOpen,onClose:e.onClose,trigger:n.createElement(f,{active:e.active||e.open},e.name),position:"left top",arrow:!1},e.options?n.createElement(v,{onMouseDown:u,onTouchStart:u},e.options?.map(i=>n.createElement(f,{active:t.has(i.id),key:i.id,onClick:s=>{if(e.onChange)if(u(s),t.has(i.id)){const r=[...d],a=r.findIndex(m=>m.id===i.id);r.splice(a,1),e.onChange(r)}else e.onChange([...d,i])}},i.name))):null,e.renderSubmenu?e.renderSubmenu():null)},u=e=>{e.stopPropagation()},k=l.div`
1
+ import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-popup";import l from"styled-components";import{useQuery as N}from"@tanstack/react-query";import{DateTimeRangePicker as R}from"@wojtekmaj/react-datetimerange-picker";import"@wojtekmaj/react-datetimerange-picker/dist/DateTimeRangePicker.css";import{fetch as S}from"../../../../../client/app/utils";import{PopupMenuUl as I}from"./PopupMenuUl";const A=[{name:"OTHER",id:"OTHER"},{name:"CONNECT",id:"CONNECT"},{name:"DELETE",id:"DELETE"},{name:"GET",id:"GET"},{name:"HEAD",id:"HEAD"},{name:"OPTIONS",id:"OPTIONS"},{name:"PATCH",id:"PATCH"},{name:"POST",id:"POST"},{name:"PUT",id:"PUT"},{name:"TRACE",id:"TRACE"}],y=[{type:"predefined",name:"last 5m",fromDateOffset:1e3*60*5},{type:"predefined",name:"last 1h",fromDateOffset:1e3*60*60},{type:"predefined",name:"last 3h",fromDateOffset:1e3*60*60*3},{type:"predefined",name:"last 12h",fromDateOffset:1e3*60*60*12},{type:"predefined",name:"last 1d",fromDateOffset:1e3*60*60*24},{type:"predefined",name:"last 3d",fromDateOffset:1e3*60*60*24*3},{type:"predefined",name:"last 7d",fromDateOffset:1e3*60*60*24*7},{type:"predefined",name:"last 30d",fromDateOffset:1e3*60*60*24*30}],P=[{name:"100 CONTINUE",id:"100"},{name:"101 SWITCHING PROTOCOLS",id:"101"},{name:"102 PROCESSING",id:"102"},{name:"200 OK",id:"200"},{name:"201 CREATED",id:"201"},{name:"202 ACCEPTED",id:"202"},{name:"203 NON AUTHORITATIVE INFORMATION",id:"203"},{name:"204 NO CONTENT",id:"204"},{name:"205 RESET CONTENT",id:"205"},{name:"206 PARTIAL CONTENT",id:"206"},{name:"207 MULTI STATUS",id:"207"},{name:"300 MULTIPLE CHOICES",id:"300"},{name:"301 MOVED PERMANENTLY",id:"301"},{name:"302 FOUND",id:"302"},{name:"303 SEE OTHER",id:"303"},{name:"304 NOT MODIFIED",id:"304"},{name:"305 USE PROXY",id:"305"},{name:"307 TEMPORARY REDIRECT",id:"307"},{name:"400 BAD REQUEST",id:"400"},{name:"401 UNAUTHORIZED",id:"401"},{name:"402 PAYMENT REQUIRED",id:"402"},{name:"403 FORBIDDEN",id:"403"},{name:"404 NOT FOUND",id:"404"},{name:"405 METHOD NOT ALLOWED",id:"405"},{name:"406 NOT ACCEPTABLE",id:"406"},{name:"407 PROXY AUTHENTICATION REQUIRED",id:"407"},{name:"408 REQUEST TIMEOUT",id:"408"},{name:"409 CONFLICT",id:"409"},{name:"410 GONE",id:"410"},{name:"411 LENGTH REQUIRED",id:"411"},{name:"412 PRECONDITION FAILED",id:"412"},{name:"413 REQUEST ENTITY TOO LARGE",id:"413"},{name:"414 REQUEST URI TOO LONG",id:"414"},{name:"415 UNSUPPORTED MEDIA TYPE",id:"415"},{name:"416 REQUESTED RANGE NOT SATISFIABLE",id:"416"},{name:"417 EXPECTATION FAILED",id:"417"},{name:"422 UNPROCESSABLE ENTITY",id:"422"},{name:"423 LOCKED",id:"423"},{name:"424 FAILED DEPENDENCY",id:"424"},{name:"429 TOO MANY REQUESTS",id:"429"},{name:"500 INTERNAL SERVER ERROR",id:"500"},{name:"501 NOT IMPLEMENTED",id:"501"},{name:"502 BAD GATEWAY",id:"502"},{name:"503 SERVICE UNAVAILABLE",id:"503"},{name:"504 GATEWAY TIMEOUT",id:"504"},{name:"505 HTTP VERSION NOT SUPPORTED",id:"505"},{name:"507 INSUFFICIENT STORAGE",id:"507"}];function x(e){return n.createElement(L,null,n.createElement(w,{onFiltersChange:e.onFiltersChange,activeFilters:e.activeFilters}),n.createElement(U,null))}var j=n.memo(x);const U=()=>null,w=n.memo(e=>{const{onFiltersChange:d,activeFilters:t}=e,{data:i}=N({queryKey:["API-PRODUCTS"],queryFn:()=>S("/api/api-keys/api-products").then(o=>o.json())}),[s,r]=O(),a=()=>r(void 0),m=(o,g)=>{const T={...t,[o]:g};d(T)};return n.createElement(h,{trigger:n.createElement(k,null,"Filter"),position:"bottom right"},n.createElement(b,null,n.createElement(E,{open:s==="api",name:"API name",options:i||[],activeOptions:t.api,onOpen:()=>r("api"),onClose:a,onChange:o=>m("api",o),active:(t.api?.length??0)>0}),n.createElement(E,{open:s==="status",name:"Status",options:P,activeOptions:t.status,onOpen:()=>r("status"),onClose:a,onChange:o=>m("status",o),active:(t.status?.length??0)>0}),n.createElement(E,{open:s==="method",name:"Method",options:A,activeOptions:t.method,onOpen:()=>r("method"),onClose:a,onChange:o=>m("method",o),active:(t.method?.length??0)>0}),n.createElement(E,{name:"Date range",open:s==="date",onOpen:()=>r("date"),onClose:a,active:!!t.date,renderSubmenu:()=>n.createElement(M,{onChange:o=>m("date",o),selectedDateFilter:t.date})}),n.createElement(_,null),n.createElement(H,{onClick:()=>d({})},"Clear filters")))}),M=e=>{const{selectedDateFilter:d,onChange:t}=e,i=d?.type==="custom",[s,r]=O(!1),[a,m]=O([null,null]),o=a?.[0]?a[0]:null,g=a?.[1]?a[1]:null,T=()=>{if(!a?.[0]&&!a?.[1])return t();const c=a[1]||new Date,p=a[0]||new Date(c.getDate()-1e3*60*60*24);r(!1),t({type:"custom",fromDate:p.getTime(),toDate:c.getTime()})};return n.createElement(v,{onMouseDown:u,onTouchStart:u},n.createElement(E,{open:s,active:i,name:"Custom range",onOpen:()=>r(!0),onClose:()=>r(!1),renderSubmenu:()=>n.createElement(F,null,n.createElement(Y,{onClick:T},"Submit"),n.createElement(_,null),n.createElement(R,{isCalendarOpen:!0,format:"yyyy-MM-dd HH:mm",closeWidgets:!1,dayPlaceholder:"DD",monthPlaceholder:"MM",yearPlaceholder:"YYYY",hourPlaceholder:"HH",minutePlaceholder:"mm",disableClock:!0,value:[o,g],onChange:c=>m(c)}))}),y.map(c=>{const p=d?.type==="predefined"&&d.name===c.name;return n.createElement(f,{active:p,key:c.name,onClick:C=>{u(C),t(p?void 0:c)}},c.name)}))},E=e=>{const d=e.activeOptions||[],t=D(()=>new Set(d.map(i=>i.id)),[e.activeOptions]);return n.createElement(h,{open:e.open,onOpen:e.onOpen,onClose:e.onClose,trigger:n.createElement(f,{active:e.active||e.open},e.name),position:"left top",arrow:!1},e.options?n.createElement(v,{onMouseDown:u,onTouchStart:u},e.options?.map(i=>n.createElement(f,{active:t.has(i.id),key:i.id,onClick:s=>{if(e.onChange)if(u(s),t.has(i.id)){const r=[...d],a=r.findIndex(m=>m.id===i.id);r.splice(a,1),e.onChange(r)}else e.onChange([...d,i])}},i.name))):null,e.renderSubmenu?e.renderSubmenu():null)},u=e=>{e.stopPropagation()},k=l.div`
2
2
  color: var(--text-color-primary);
3
3
  border-radius: var(--border-radius);
4
4
  cursor: pointer;
@@ -14,7 +14,7 @@ import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-
14
14
  `,L=l.div`
15
15
  display: flex;
16
16
  gap: var(--spacing-xxs);
17
- `,_=l(I)`
17
+ `,b=l(I)`
18
18
  padding: var(--spacing-xs);
19
19
  width: 288px;
20
20
  li {
@@ -32,7 +32,7 @@ import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-
32
32
  margin-bottom: 2px;
33
33
  }
34
34
  }
35
- `,v=l(_)`
35
+ `,v=l(b)`
36
36
  max-height: 200px;
37
37
  overflow-y: scroll;
38
38
  &::-webkit-scrollbar {
@@ -96,7 +96,7 @@ import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-
96
96
  }
97
97
 
98
98
  .react-calendar__tile--now {
99
- color: var(--color-primary-base);
99
+ color: var(--color-primary-base, var(--color-blueberry-6));
100
100
  }
101
101
 
102
102
  .react-datetimerange-picker__inputGroup__input:invalid {
@@ -146,7 +146,7 @@ import n,{useMemo as D,useState as O}from"react";import{Popup as h}from"reactjs-
146
146
  font-size: var(--font-size-lg);
147
147
  text-align: center;
148
148
  margin: 0 var(--spacing-xs);
149
- `,b=l.div`
149
+ `,_=l.div`
150
150
  margin: var(--spacing-xs);
151
151
  width: auto;
152
152
  min-width: auto;
@@ -28,7 +28,7 @@ import o,{css as t}from"styled-components";const a=o.div`
28
28
 
29
29
  &:hover,
30
30
  &:focus {
31
- border-color: var(--color-primary-base);
31
+ border-color: var(--color-primary-base, var(--color-blueberry-6));
32
32
  }
33
33
 
34
34
  &::placeholder {
@@ -1 +1 @@
1
- import i from"react";import c from"react-select";import{useTranslate as d}from"../../../../../client/app/hooks";const m=function(o){const{options:r,defaultValue:e,onChange:a}=o,{translate:l}=d();return i.createElement(c,{isMulti:!0,defaultValue:e,options:r,closeMenuOnSelect:!1,styles:p,placeholder:l("dev.select.placeholder","Empty"),onChange:t=>{a(t.map(n=>n.value))}})},p={control:o=>({...o,border:"1px solid var(--border-color-primary)",boxShadow:"none",backgroundColor:"var(--bg-color-raised)","&:hover":{borderColor:"var(--color-primary-base)",cursor:"pointer"}}),placeholder:o=>({...o,color:"var(--input-content-placeholder-color)",fontSize:"var(--font-size-lg)"}),indicatorsContainer:()=>({display:"none"}),menu:o=>({...o,padding:"var(--spacing-xxs)",margin:"0",boxShadow:"var(--bg-raised-shadow)",border:"1px solid var(--border-color-primary)",backgroundColor:"var(--bg-color)"}),menuList:o=>({...o,paddingBottom:0,paddingTop:0}),option:o=>({...o,cursor:"pointer",borderRadius:"4px",color:"var(--text-color-primary)",backgroundColor:"transparent",fontSize:"14px","&:hover":{backgroundColor:"var(--bg-color-hover)"}}),multiValue:o=>({...o,backgroundColor:"var(--bg-color-raised)",color:"var(--text-color-primary)",border:"1px solid var(--border-color-primary)",borderRadius:"32px",height:"32px",alignItems:"center"}),multiValueLabel:o=>({...o,fontSize:"14px",color:"var(--text-color-primary)",paddingLeft:"var(--spacing-sm)"}),valueContainer:o=>({...o,padding:"6px 8px"}),multiValueRemove:o=>({...o,padding:0,margin:"0 var(--spacing-xxs)","&:hover":{backgroundColor:"var(--bg-color-raised)",color:"var(--text-color-secondary)"}})};export{m as MultiSelect};
1
+ import i from"react";import c from"react-select";import{useTranslate as d}from"../../../../../client/app/hooks";const v=function(r){const{options:o,defaultValue:e,onChange:a}=r,{translate:l}=d();return i.createElement(c,{isMulti:!0,defaultValue:e,options:o,closeMenuOnSelect:!1,styles:p,placeholder:l("dev.select.placeholder","Empty"),onChange:t=>{a(t.map(n=>n.value))}})},p={control:r=>({...r,border:"1px solid var(--border-color-primary)",boxShadow:"none",backgroundColor:"var(--bg-color-raised)","&:hover":{borderColor:"var(--color-primary-base, var(--color-blueberry-6))",cursor:"pointer"}}),placeholder:r=>({...r,color:"var(--input-content-placeholder-color)",fontSize:"var(--font-size-lg)"}),indicatorsContainer:()=>({display:"none"}),menu:r=>({...r,padding:"var(--spacing-xxs)",margin:"0",boxShadow:"var(--bg-raised-shadow)",border:"1px solid var(--border-color-primary)",backgroundColor:"var(--bg-color)"}),menuList:r=>({...r,paddingBottom:0,paddingTop:0}),option:r=>({...r,cursor:"pointer",borderRadius:"4px",color:"var(--text-color-primary)",backgroundColor:"transparent",fontSize:"14px","&:hover":{backgroundColor:"var(--bg-color-hover)"}}),multiValue:r=>({...r,backgroundColor:"var(--bg-color-raised)",color:"var(--text-color-primary)",border:"1px solid var(--border-color-primary)",borderRadius:"32px",height:"32px",alignItems:"center"}),multiValueLabel:r=>({...r,fontSize:"14px",color:"var(--text-color-primary)",paddingLeft:"var(--spacing-sm)"}),valueContainer:r=>({...r,padding:"6px 8px"}),multiValueRemove:r=>({...r,padding:0,margin:"0 var(--spacing-xxs)","&:hover":{backgroundColor:"var(--bg-color-raised)",color:"var(--text-color-secondary)"}})};export{v as MultiSelect};
@@ -1,13 +1,13 @@
1
- import*as r from"react";import t from"styled-components";function i(e){return r.createElement(l,null,e.label?r.createElement("span",null,e.label):null,r.createElement(o,{...e.inputProps}))}const o=t.input`
1
+ import*as e from"react";import o from"styled-components";function i(r){return e.createElement(t,null,r.label?e.createElement("span",null,r.label):null,e.createElement(l,{...r.inputProps}))}const l=o.input`
2
2
  border: 1px solid var(--border-color-primary);
3
3
  padding: var(--spacing-sm);
4
4
  border-radius: var(--border-radius);
5
5
  width: 100%;
6
6
  height: 48px;
7
7
  display: block;
8
- outline-color: var(--color-primary-base);
8
+ outline-color: var(--color-primary-base, var(--color-blueberry-6));
9
9
  transition: outline 0.25s ease;
10
- `,l=t.label`
10
+ `,t=o.label`
11
11
  color: var(--text-color-primary);
12
12
  span {
13
13
  font-size: 0.9em;
@@ -1 +1 @@
1
- import{envConfig as o}from"../../config/env-config.js";async function i(r){return{id:"enforce-login",async processContent(n){if(o.REDOCLY_ENFORCE_LOGIN!=="true")return;const e=n.getConfig();n.setGlobalConfig({access:{...e.access,requiresLogin:!0,rbac:{}}})}}}export{i as enforceLoginPlugin};
1
+ import{envConfig as r}from"../../config/env-config.js";async function f(c){return{id:"enforce-login",async processContent(o){const i=!!r.REDOCLY_ENFORCE_RESIDENCY,n=r.REDOCLY_ENFORCE_LOGIN==="true";if(!i&&!n)return;const e=o.getConfig();o.setGlobalConfig({...n&&"rbac"in e&&{rbac:void 0},...n&&"requiresLogin"in e&&{requiresLogin:void 0},...i&&"residency"in e&&{residency:void 0},access:{...e.access,...i&&{residency:r.REDOCLY_ENFORCE_RESIDENCY},...n&&{requiresLogin:!0,rbac:void 0}}})}}}export{f as enforceLoginPlugin};
@@ -1,2 +1,2 @@
1
- import x from"@redocly/portal-plugin-mock-server";import B from"path";import{REDOCLY_ROUTE_RBAC as O}from"@redocly/config";import{combineUrls as v,withPathPrefix as N}from"@redocly/theme/core/utils";import{VERSION_SEPARATOR as L}from"../constants/common.js";import{removeTrailingSlash as F}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as V}from"../../utils/url/remove-leading-slash.js";import{isTruthy as j}from"../../utils/guards/is-truthy.js";import{normalizeRouteSlug as T}from"../../utils/path/normalize-route-slug.js";import{slash as k}from"../../utils/path/slash.js";import{parsePathVersions as $}from"../../utils/path/parse-path-versions.js";import{reporter as E}from"../tools/notifiers/reporter.js";import{logger as P}from"../tools/notifiers/logger.js";import{envConfig as A}from"../config/env-config.js";import{shaDirPathShort as z}from"../utils/crypto/sha-dir-path-short.js";import{customPagesPlugin as M}from"../../server/plugins/pages/index.js";import{openAPIDocsPlugin as q}from"../../server/plugins/openapi-docs/index.js";import{asyncAPIDocsPlugin as I}from"../../server/plugins/asyncapi-docs/index.js";import{configParserPlugin as U}from"./config-parser/index.js";import{markdownPlugin as G}from"./markdown/index.js";import{generateBrowserPluginsModule as W,generateClientRoutes as K,generateTemplatesModule as H}from"../esbuild/generate.js";import{graphqlDocsPlugin as Y}from"./graphql-docs/index.js";import{searchPlugin as J}from"./search/index.js";import{defaultThemePlugin as Q}from"./default-theme/index.js";import{apiKeyMgmtPlugin as X}from"./dev-onboarding/index.js";import{apiFunctionsPlugin as Z}from"./api-functions/index.js";import{scorecardClassicPlugin as ee}from"./scorecard-classic/index.js";import{lintPlugin as te}from"./lint/index.js";import{resolveExternalPlugins as oe}from"../external-plugins/resolve-external-plugins.js";import{sidebarsPlugin as ie}from"./sidebars/index.js";import{l10nPlugin as ne}from"./l10n/index.js";import{analyticsPlugins as re}from"./analytics/index.js";import{sitemapPlugin as se}from"./sitemap/index.js";import{entitlementsPlugin as ae}from"./entitlements/index.js";import{getBilledPagesCount as le,slug as ce}from"../utils/index.js";import{telemetry as ue}from"../../cli/telemetry/index.js";import{telemetryTraceStep as b}from"../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as D}from"../entitlements/entitlements-provider.js";import{enforceLoginPlugin as fe}from"./enforce-login/index.js";import{ssoPlugin as me}from"./sso/index.js";import{Cache as de}from"../fs/cache.js";import{ContentFs as ge}from"../fs/content-fs.js";import{findProductBySlug as pe}from"../utils/product.js";import{parseBaseName as Pe}from"./utils.js";import{isRouteReserved as he}from"./get-reserved-routes.js";import{catalogClassicPlugin as ye}from"./catalog-classic/index.js";import{arazzoDocsPlugin as Ce}from"./arazzo-docs/index.js";const we=[U,fe,me,Q,ne,G,q,I,Ce,ee,te,Z,M,Y,X,ye,{importPath:"./catalog-entities/plugin.js",loadCondition:()=>A.NEW_CATALOG_ENABLED==="true"},{importPath:"./scorecards/plugin.js",loadCondition:()=>A.NEW_SCORECARDS_ENABLED==="true"},{importPath:"./mcp/index.js",loadCondition:()=>D.instance().canAccessFeature("mcp")},ie,se,ae,J,x,...re];async function ht(o,t=we){return await b("build.plugins_init",async()=>{const n=[],w=D.instance(),h={};for(const f of t){let a;if("loadCondition"in f){if(!f.loadCondition())continue;const e=await import(f.importPath);if(!e.default||typeof e.default!="function"){await E.panicOnBuild(`Dynamic plugin ${f.importPath} does not export a default export or it is not a function`);continue}a=e.default}else a=f;const d=await a(o);for(const[e,i]of Object.entries(d.loaders||{}))h[e]&&await E.panicOnBuild(`Duplicate loader with name ${e}`),h[e]=i;(!d.requiredEntitlements||d.requiredEntitlements?.every(e=>w.canAccessFeature(e)))&&n.push(d)}const s=new ge(o.contentDir);await s.ready;const u=new de(s);return u.setLoaders(h),P.verbose("All plugins instantiated"),{pluginInstances:n,lifecycleContext:Ee(s,u)}})}function _(o){P.verbose("Generating templates"),b("build.write_client_entries",()=>{H(o),W(o),K(o)})}async function Re(o){const t=o.getAllRoutes();le(t)||await E.panicOnBuild("No routes created by plugins. Please check your project configuration.")}async function yt(o,t,n,w={}){const h=D.instance();try{await b("build.plugins_run",async()=>{t.startPluginsRun();let s=[];await b("build.plugins_process_content",async a=>{for(const e of o){const i=P.startTiming();await e.processContent?.(t,n),P.verboseTime(i,`processContent for ${e.id}`)}s=(await oe(t.contentDir,t.config.plugins)).map(e=>e.lifecyclePlugin).filter(e=>j(e)&&(!e.requiredEntitlements||e.requiredEntitlements?.every(i=>h.canAccessFeature(i)))),n.cache.setLoaders(Object.fromEntries(s.flatMap(e=>Object.entries(e.loaders||{}))));for(const e of s){const i=P.startTiming();await e.processContent?.(t,n),P.verboseTime(i,`processContent for ${e.id}`)}a?.setAttribute("externalPluginsProcessed",s.length.toString())});const u=await ve(t,n),f=[...o,...s];_(t),A.isDevelopMode?await t.userCodeReady:t.buildRevision++,await b("build.plugins_after_routes_created",async()=>{for(const a of f){const d=P.startTiming();await a.afterRoutesCreated?.(t,u),P.verboseTime(d,`afterRoutesCreated for ${a.id}`)}}),await t.reportUnsetEnvVars(),await Re(t),_(t)})}catch(s){const u="Unhandled error in plugin. "+s.message+`
2
- `+s.stack;ue.sendCliErrorCaughtMessage({message:u}),w.failFast?await E.panic(u):await E.panicOnBuild(u)}finally{t.finishPluginsRun()}}async function ve(o,t){const{cache:n,fs:w}=t,h=await t.getConfig(),s=Object.values(h.products||{}),u=(await n.load("versions-config","versions-config")).data,f=(await n.load("content-slugs","content-slugs")).data;for(const{duplicateInAllLocales:e,slugSuffix:i,sharedData:y=[],redirectFrom:C=[],...l}of o.newRoutes){const c=T(l.slug?l.slug:a(l.fsPath)),g=F(i?v(c,i):c),r={...l,[O]:{slug:g,fsPath:l.fsPath},slug:g,baseSlug:c,versions:d(l.fsPath,i),product:pe(s,g,a)};he(g,h)&&await E.panicOnBuildContentError(`Route "${g}" is reserved and cannot be used. Please choose a different slug for "${l.fsPath}".`),o.routesByFsPath.set(r.fsPath,r.slug),o.routesBySlug.set(r.slug,r);for(const{key:p,id:m}of y)o.addRouteSharedData(r.slug,p,m);for(const p of C)o.addRedirect(p.from,{type:p.type,to:r.slug});if(e)for(const p of w.localeFolders){const m={...r,slug:"/"+p.toLowerCase()+r.slug};o.routesByFsPath.set(m.fsPath,m.slug),o.routesBySlug.set(m.slug,m);for(const{key:R,id:S}of y)o.addRouteSharedData(m.slug,R,S);for(const R of C){const S=v(w.localizationFolder,p,R.from);o.addRedirect(S,{type:R.type,to:m.slug})}}}return o.newRoutes=[],{...t,slugify:a};function a(e){const i=k(e).replace(new RegExp("^(@i18n|@l10n)\\/"),""),y=f.fileSlugs.get(i)||f.dirSlugs.get(F(i));if(y)return y;const{baseName:C,isIndexFile:l}=Pe(e);let c=B.dirname(e.replace(new RegExp("^(@i18n|@l10n)\\/"),""));return c=c==="."?"/":c,v("/",ce(l?c:v(c,C)),"/")}function d(e,i=""){const y=$(e);if(!y?.versionName)return;const{versionFolderPath:C,filePathInVersion:l,versionName:c}=y,g=u.get(C);if(g)return g.versions.map(r=>{const p=g.defaultVersion===r.version,m=V(v(C,L+r.version,l)),R=a(m);return{version:r.version,label:r.name||r.version,link:T(v(R,i)),default:p,active:c===r.version,folderId:z(`${C}`)}})}}function Ee(o,t){return{fs:o,cache:t,getConfig:async(n=".")=>(await t.load(n,"nearest-redocly-config",o.localeFolders)).data,isPathIgnored:async n=>(await t.load(n,"is-ignored")).data,withPathPrefix:N,logger:P}}export{we as INTERNAL_PLUGINS,Ee as createLifecycleContext,ht as initPlugins,yt as runPlugins,_ as writeClientEntries};
1
+ import x from"@redocly/portal-plugin-mock-server";import B from"path";import{REDOCLY_ROUTE_RBAC as O}from"@redocly/config";import{combineUrls as v,withPathPrefix as N}from"@redocly/theme/core/utils";import{VERSION_SEPARATOR as L}from"../constants/common.js";import{removeTrailingSlash as F}from"../../utils/url/remove-trailing-slash.js";import{removeLeadingSlash as k}from"../../utils/url/remove-leading-slash.js";import{isTruthy as V}from"../../utils/guards/is-truthy.js";import{normalizeRouteSlug as T}from"../../utils/path/normalize-route-slug.js";import{slash as j}from"../../utils/path/slash.js";import{parsePathVersions as $}from"../../utils/path/parse-path-versions.js";import{reporter as E}from"../tools/notifiers/reporter.js";import{logger as P}from"../tools/notifiers/logger.js";import{envConfig as A}from"../config/env-config.js";import{shaDirPathShort as z}from"../utils/crypto/sha-dir-path-short.js";import{customPagesPlugin as M}from"../../server/plugins/pages/index.js";import{openAPIDocsPlugin as q}from"../../server/plugins/openapi-docs/index.js";import{asyncAPIDocsPlugin as I}from"../../server/plugins/asyncapi-docs/index.js";import{configParserPlugin as U}from"./config-parser/index.js";import{markdownPlugin as G}from"./markdown/index.js";import{generateBrowserPluginsModule as W,generateClientRoutes as K,generateTemplatesModule as H}from"../esbuild/generate.js";import{graphqlDocsPlugin as Y}from"./graphql-docs/index.js";import{searchPlugin as J}from"./search/index.js";import{defaultThemePlugin as Q}from"./default-theme/index.js";import{apiKeyMgmtPlugin as X}from"./dev-onboarding/index.js";import{apiFunctionsPlugin as Z}from"./api-functions/index.js";import{scorecardClassicPlugin as ee}from"./scorecard-classic/index.js";import{lintPlugin as te}from"./lint/index.js";import{resolveExternalPlugins as oe}from"../external-plugins/resolve-external-plugins.js";import{sidebarsPlugin as ie}from"./sidebars/index.js";import{l10nPlugin as ne}from"./l10n/index.js";import{validateRedirects as re}from"../utils/redirects/validate-redirects.js";import{analyticsPlugins as se}from"./analytics/index.js";import{sitemapPlugin as ae}from"./sitemap/index.js";import{entitlementsPlugin as le}from"./entitlements/index.js";import{getBilledPagesCount as ce,slug as ue}from"../utils/index.js";import{telemetry as fe}from"../../cli/telemetry/index.js";import{telemetryTraceStep as b}from"../../cli/telemetry/helpers/trace-step.js";import{EntitlementsProvider as D}from"../entitlements/entitlements-provider.js";import{enforceLoginPlugin as me}from"./enforce-login/index.js";import{ssoPlugin as de}from"./sso/index.js";import{Cache as ge}from"../fs/cache.js";import{ContentFs as pe}from"../fs/content-fs.js";import{findProductBySlug as Pe}from"../utils/product.js";import{parseBaseName as he}from"./utils.js";import{isRouteReserved as ye}from"./get-reserved-routes.js";import{catalogClassicPlugin as Ce}from"./catalog-classic/index.js";import{arazzoDocsPlugin as we}from"./arazzo-docs/index.js";const Re=[U,me,de,Q,ne,G,q,I,we,ee,te,Z,M,Y,X,Ce,{importPath:"./catalog-entities/plugin.js",loadCondition:()=>A.NEW_CATALOG_ENABLED==="true"},{importPath:"./scorecards/plugin.js",loadCondition:()=>A.NEW_SCORECARDS_ENABLED==="true"},{importPath:"./mcp/index.js",loadCondition:()=>D.instance().canAccessFeature("mcp")},ie,ae,le,J,x,...se];async function Ct(o,t=Re){return await b("build.plugins_init",async()=>{const n=[],w=D.instance(),h={};for(const f of t){let a;if("loadCondition"in f){if(!f.loadCondition())continue;const e=await import(f.importPath);if(!e.default||typeof e.default!="function"){await E.panicOnBuild(`Dynamic plugin ${f.importPath} does not export a default export or it is not a function`);continue}a=e.default}else a=f;const d=await a(o);for(const[e,i]of Object.entries(d.loaders||{}))h[e]&&await E.panicOnBuild(`Duplicate loader with name ${e}`),h[e]=i;(!d.requiredEntitlements||d.requiredEntitlements?.every(e=>w.canAccessFeature(e)))&&n.push(d)}const s=new pe(o.contentDir);await s.ready;const u=new ge(s);return u.setLoaders(h),P.verbose("All plugins instantiated"),{pluginInstances:n,lifecycleContext:be(s,u)}})}function _(o){P.verbose("Generating templates"),b("build.write_client_entries",()=>{H(o),W(o),K(o)})}async function ve(o){const t=o.getAllRoutes();ce(t)||await E.panicOnBuild("No routes created by plugins. Please check your project configuration.")}async function wt(o,t,n,w={}){const h=D.instance();try{await b("build.plugins_run",async()=>{t.startPluginsRun();let s=[];await b("build.plugins_process_content",async a=>{for(const e of o){const i=P.startTiming();await e.processContent?.(t,n),P.verboseTime(i,`processContent for ${e.id}`)}s=(await oe(t.contentDir,t.config.plugins)).map(e=>e.lifecyclePlugin).filter(e=>V(e)&&(!e.requiredEntitlements||e.requiredEntitlements?.every(i=>h.canAccessFeature(i)))),n.cache.setLoaders(Object.fromEntries(s.flatMap(e=>Object.entries(e.loaders||{}))));for(const e of s){const i=P.startTiming();await e.processContent?.(t,n),P.verboseTime(i,`processContent for ${e.id}`)}a?.setAttribute("externalPluginsProcessed",s.length.toString())});const u=await Ee(t,n),f=[...o,...s];_(t),A.isDevelopMode?await t.userCodeReady:t.buildRevision++,await b("build.plugins_after_routes_created",async()=>{for(const a of f){const d=P.startTiming();await a.afterRoutesCreated?.(t,u),P.verboseTime(d,`afterRoutesCreated for ${a.id}`)}}),re(t),await t.reportUnsetEnvVars(),await ve(t),_(t)})}catch(s){const u="Unhandled error in plugin. "+s.message+`
2
+ `+s.stack;fe.sendCliErrorCaughtMessage({message:u}),w.failFast?await E.panic(u):await E.panicOnBuild(u)}finally{t.finishPluginsRun()}}async function Ee(o,t){const{cache:n,fs:w}=t,h=await t.getConfig(),s=Object.values(h.products||{}),u=(await n.load("versions-config","versions-config")).data,f=(await n.load("content-slugs","content-slugs")).data;for(const{duplicateInAllLocales:e,slugSuffix:i,sharedData:y=[],redirectFrom:C=[],...l}of o.newRoutes){const c=T(l.slug?l.slug:a(l.fsPath)),g=F(i?v(c,i):c),r={...l,[O]:{slug:g,fsPath:l.fsPath},slug:g,baseSlug:c,versions:d(l.fsPath,i),product:Pe(s,g,a)};ye(g,h)&&await E.panicOnBuildContentError(`Route "${g}" is reserved and cannot be used. Please choose a different slug for "${l.fsPath}".`),o.routesByFsPath.set(r.fsPath,r.slug),o.routesBySlug.set(r.slug,r);for(const{key:p,id:m}of y)o.addRouteSharedData(r.slug,p,m);for(const p of C)o.addRedirect(p.from,{type:p.type,to:r.slug});if(e)for(const p of w.localeFolders){const m={...r,slug:"/"+p.toLowerCase()+r.slug};o.routesByFsPath.set(m.fsPath,m.slug),o.routesBySlug.set(m.slug,m);for(const{key:R,id:S}of y)o.addRouteSharedData(m.slug,R,S);for(const R of C){const S=v(w.localizationFolder,p,R.from);o.addRedirect(S,{type:R.type,to:m.slug},{trackOriginalSource:!1})}}}return o.newRoutes=[],{...t,slugify:a};function a(e){const i=j(e).replace(new RegExp("^(@i18n|@l10n)\\/"),""),y=f.fileSlugs.get(i)||f.dirSlugs.get(F(i));if(y)return y;const{baseName:C,isIndexFile:l}=he(e);let c=B.dirname(e.replace(new RegExp("^(@i18n|@l10n)\\/"),""));return c=c==="."?"/":c,v("/",ue(l?c:v(c,C)),"/")}function d(e,i=""){const y=$(e);if(!y?.versionName)return;const{versionFolderPath:C,filePathInVersion:l,versionName:c}=y,g=u.get(C);if(g)return g.versions.map(r=>{const p=g.defaultVersion===r.version,m=k(v(C,L+r.version,l)),R=a(m);return{version:r.version,label:r.name||r.version,link:T(v(R,i)),default:p,active:c===r.version,folderId:z(`${C}`)}})}}function be(o,t){return{fs:o,cache:t,getConfig:async(n=".")=>(await t.load(n,"nearest-redocly-config",o.localeFolders)).data,isPathIgnored:async n=>(await t.load(n,"is-ignored")).data,withPathPrefix:N,logger:P}}export{Re as INTERNAL_PLUGINS,be as createLifecycleContext,Ct as initPlugins,wt as runPlugins,_ as writeClientEntries};
@@ -1,21 +1,29 @@
1
- import{isPrimitive as w}from"../../../../utils/guards/is-primitive.js";import{getNodeAttribute as t}from"../../../../markdoc/helpers/get-node-attribute.js";import{getVariable as C}from"../../../../markdoc/helpers/get-variable.js";import{isTag as E}from"../../../../markdoc/helpers/guards/is-tag.js";import{isNode as N}from"../../../../markdoc/helpers/guards/is-node.js";import{isConditionalNode as v}from"../../../../markdoc/helpers/guards/is-conditional-node.js";import{isVariable as A}from"../../../../markdoc/helpers/guards/is-variable.js";import{isExampleNode as S}from"../../../../markdoc/helpers/guards/is-example-node.js";import{isFenceNode as x}from"../../../../markdoc/helpers/guards/is-fence-node.js";function a(s,r={}){const n=[];for(const e of s){if(w(e)){const i=r.isTrim?u(String(e)):String(e);i&&n.push(i)}else if(N(e))switch(e.type){case"text":n.push(a([t(e,"content")],r));break;case"code":n.push(`\`${a([t(e,"content")],r)}\``);break;case"blockquote":n.push(`> ${a(e.children,r)}
2
- `);break;case"fence":const i=t(e,"process")===!1;n.push(o(e,r,i));break;case"list":const h=t(e,"ordered")||!1,d=t(e,"marker"),f=e.children.map((c,l)=>`${" ".repeat((r?.indent??0)*2)}${h?l+1:""}${d} ${a([c],{...r??{},indent:(r?.indent??0)+1})}`);n.push(`${f.join("")}
3
- `);break;case"item":n.push(e.children.map(c=>`${a([c],r).trimEnd()}
4
- `).join(""));break;case"thead":const $=e.children.map(c=>a([c],r));n.push(`${$}| ${" --- |".repeat(e.children?.[0]?.children.length)}
5
- `);break;case"tr":const p=e.children.map(c=>a([c],r));n.push(`| ${p.join(" | ")} |
6
- `);break;case"em":n.push(`*${a(e.children,r)}*`);break;case"strong":n.push(`**${a(e.children,r)}**`);break;case"softbreak":case"hardbreak":n.push(`
7
- `);break;case"hr":n.push(`
1
+ import{isPrimitive as v}from"../../../../utils/guards/is-primitive.js";import{getNodeAttribute as c}from"../../../../markdoc/helpers/get-node-attribute.js";import{getVariable as T}from"../../../../markdoc/helpers/get-variable.js";import{isTag as C}from"../../../../markdoc/helpers/guards/is-tag.js";import{isNode as N}from"../../../../markdoc/helpers/guards/is-node.js";import{isConditionalNode as A}from"../../../../markdoc/helpers/guards/is-conditional-node.js";import{isVariable as E}from"../../../../markdoc/helpers/guards/is-variable.js";import{isExampleNode as w}from"../../../../markdoc/helpers/guards/is-example-node.js";import{isFenceNode as x}from"../../../../markdoc/helpers/guards/is-fence-node.js";function s(a,n={}){const r=[];for(const e of a){if(v(e)){const i=n.isTrim?d(String(e)):String(e);i&&r.push(i)}else if(N(e))switch(e.type){case"text":r.push(s([c(e,"content")],n));break;case"code":r.push(`\`${s([c(e,"content")],n)}\``);break;case"blockquote":r.push(`> ${s(e.children,n)}
2
+ `);break;case"fence":const i=c(e,"process")===!1;r.push(f(e,n,i));break;case"list":const l=c(e,"ordered")||!1,o=c(e,"marker"),u=e.children.map((t,h)=>`${" ".repeat((n?.indent??0)*2)}${l?h+1:""}${o} ${s([t],{...n??{},indent:(n?.indent??0)+1})}`);r.push(`${u.join("")}
3
+ `);break;case"item":r.push(e.children.map(t=>`${s([t],n).trimEnd()}
4
+ `).join(""));break;case"thead":const p=e.children.map(t=>s([t],n));r.push(`${p}| ${" --- |".repeat(e.children?.[0]?.children.length)}
5
+ `);break;case"tr":const $=e.children.map(t=>s([t],n));r.push(`| ${$.join(" | ")} |
6
+ `);break;case"em":r.push(`*${s(e.children,n)}*`);break;case"strong":r.push(`**${s(e.children,n)}**`);break;case"softbreak":case"hardbreak":r.push(`
7
+ `);break;case"hr":r.push(`
8
8
 
9
9
  ---
10
10
 
11
- `);break;case"heading":const k=t(e,"level")??0;n.push(`${"#".repeat(k)} ${a(e.children,r)}
12
- `);break;case"image":const b=t(e,"alt")||"",g=t(e,"src")||"";n.push(`![${b}](${g})`);break;case"link":const T=t(e,"href")||"";n.push(`[${a(e.children,r)}](${T})`);break;case"paragraph":case"table":n.push(`${a(e.children,r)}
13
- `);break;case"tag":if(v(e)&&r.skipConditionals)continue;if(S(e)){n.push(...e.children.map(c=>{if(x(c)){const l=t(c,"process")!==!0;return o(c,r,l)}return a([c],r)}));continue}if(e.tag==="code-snippet"){const c=t(e,"rawContent");if(c){const l=t(e,"language"),m=t(e,"title"),j=`
14
- \`\`\`${`${l||""}${m?`${l?" ":""}${m}`:""}`}
15
- ${c.trimEnd()}
11
+ `);break;case"heading":const k=c(e,"level")??0;r.push(`${"#".repeat(k)} ${s(e.children,n)}
12
+ `);break;case"image":const g=c(e,"alt")||"",b=c(e,"src")||"";r.push(`![${g}](${b})`);break;case"link":const j=c(e,"href")||"";r.push(`[${s(e.children,n)}](${j})`);break;case"paragraph":case"table":r.push(`${s(e.children,n)}
13
+ `);break;case"tag":if(A(e)&&n.skipConditionals)continue;if(w(e)){r.push(...e.children.map(t=>{if(x(t)){const h=c(t,"process")!==!0;return f(t,n,h)}return s([t],n)}));continue}if(e.tag==="code-snippet"){const t=c(e,"rawContent");if(t){const h=c(e,"language"),m=c(e,"title"),S=`
14
+ \`\`\`${`${h||""}${m?`${h?" ":""}${m}`:""}`}
15
+ ${t.trimEnd()}
16
16
  \`\`\`
17
- `;n.push(j)}continue}n.push(`${a(e.children,r)}`);break;default:n.push(a(e.children,r));break}else E(e)&&n.push(a(e.children,r));A(e)&&n.push(a([C(e,r.variables)],r))}return r.isTrim?u(n.map(e=>u(e)).join("")):n.join("")}function u(s){return s.replace(/^[ \t\r\f]+|[ \t\r\f]+$/g,"")}function o(s,r={},n=!1){const e=t(s,"title"),i=t(s,"language"),h=`${i||""}${e?`${i?" ":""}${e}`:""}`,d=n?(t(s,"content")||"").trimEnd():a(s.children,r).trimEnd();return`
18
- \`\`\`${h}
19
- ${d}
17
+ `;r.push(S)}continue}if(e.tag==="json-example"){const t=y(e);t&&r.push(t);continue}if(e.tag==="json-schema"){const t=B(e);t&&r.push(t);continue}r.push(`${s(e.children,n)}`);break;default:r.push(s(e.children,n));break}else C(e)&&r.push(s(e.children,n));E(e)&&r.push(s([T(e,n.variables)],n))}return n.isTrim?d(r.map(e=>d(e)).join("")):r.join("")}function d(a){return a.replace(/^[ \t\r\f]+|[ \t\r\f]+$/g,"")}function f(a,n={},r=!1){const e=c(a,"title"),i=c(a,"language"),l=`${i||""}${e?`${i?" ":""}${e}`:""}`,o=r?(c(a,"content")||"").trimEnd():s(a.children,n).trimEnd();return`
18
+ \`\`\`${l}
19
+ ${o}
20
20
  \`\`\`
21
- `}export{a as toMarkdown};
21
+ `}function y(a){const n=c(a,"json")??c(a,"valueResolved")??c(a,"schemaResolved"),r=c(a,"title");return n===null?null:`
22
+ \`\`\`${`json${r?` ${r}`:""}`}
23
+ ${JSON.stringify(n,null,2)}
24
+ \`\`\`
25
+ `}function B(a){const n=c(a,"schemaResolved");if(n===null)return null;const{components:r,openapi:e,...i}=n,{__root:l,...o}=r?.schemas||{},u=l?{...l,...Object.keys(o).length>0?{components:{schemas:o}}:{},...i}:n;return`
26
+ \`\`\`json
27
+ ${JSON.stringify(u,null,2)}
28
+ \`\`\`
29
+ `}export{s as toMarkdown};
@@ -1 +1 @@
1
- import{logger as a}from"../../../tools/notifiers/logger.js";import{isNode as f}from"../../../../markdoc/helpers/guards/is-node.js";import{isConditionalNode as u}from"../../../../markdoc/helpers/guards/is-conditional-node.js";import{isContentNode as y}from"../../../../markdoc/helpers/guards/is-content-node.js";import{getNodeAttribute as h}from"../../../../markdoc/helpers/get-node-attribute.js";import{extractRbacFromCondition as N}from"../../../../markdoc/helpers/extract-rbac-from-condition-node.js";import{TextNode as e}from"./nodes/text-node.js";import{TAG_TITLE_ATTRIBUTES as T,TagNode as n}from"./nodes/tag-node.js";import{HeadingNode as p}from"./nodes/heading-node.js";class C{#t;#o;#i;#s;constructor({ast:i,partials:t,skipConditionals:r=!1,getInnerContent:s}){if(this.#t=i,this.#o=t,this.#i=r,this.#s=s,!this.#t||!f(this.#t))throw new Error("ast is not a valid Markdoc Node.")}*transform(){yield*this.#c(this.#t,{parentNode:null})}*#c(i,t,r=this.#s){if(!(!i||!f(i))){if(u(i)){if(this.#i)return;const s=N(i);s&&s.length>0&&(yield*this.#r(i,{...t,rbacTeams:s},r));return}if(y(i)){yield new e({node:i,content:r([i],{skipConditionals:this.#i}),...t});return}if(i.type==="heading"){yield new p({node:i,content:r([i],{skipConditionals:this.#i}),rbacTeams:t?.rbacTeams});return}if(i.type==="tag"){yield*this.#l(i,t,r);return}yield*this.#r(i,t,r)}}*#l(i,t,r=this.#s){switch(i.tag){case"partial":{const s=i.attributes.file,o=i.attributes.variables??{};if(s&&this.#o[s]){yield*this.#c(this.#o[s],t,(c,l)=>r(c,{...l,variables:o}));return}a.warn(`Could not create search indexes for partial \u201C${s}\u201D: file not found`);return}case"cards":case"tabs":case"code-walkthrough":{yield*this.#r(i,t,r);return}case"markdoc-example":{const s=r([i],{skipConditionals:this.#i});yield new e({node:i,content:s,...t});return}case"code-snippet":{const s=h(i,"title"),o=r([i],{skipConditionals:this.#i});if(s){const c=new n({node:i,content:s,...t});t={...t,parentNode:c},yield c}o&&(yield new e({node:i,content:o,...t}));return}default:{const s=T.find(l=>l in i.attributes),o=(s&&h(i,s))??"",c=typeof o=="string"?o:r([o]);if(c){const l=new n({node:i,content:c,...t});t={...t,parentNode:l},yield l}yield*this.#r(i,t,r);return}}}*#r(i,t,r=this.#s){for(const s of[...Object.values(i.slots),...i.children])for(const o of this.#c(s,t,r))o instanceof p&&(t={...t,parentNode:o}),yield o}}export{C as AstToSearchNodeTransformer};
1
+ import{logger as u}from"../../../tools/notifiers/logger.js";import{isNode as f}from"../../../../markdoc/helpers/guards/is-node.js";import{isConditionalNode as y}from"../../../../markdoc/helpers/guards/is-conditional-node.js";import{isContentNode as N}from"../../../../markdoc/helpers/guards/is-content-node.js";import{getNodeAttribute as n}from"../../../../markdoc/helpers/get-node-attribute.js";import{extractRbacFromCondition as T}from"../../../../markdoc/helpers/extract-rbac-from-condition-node.js";import{TextNode as e}from"./nodes/text-node.js";import{TAG_TITLE_ATTRIBUTES as b,TagNode as p}from"./nodes/tag-node.js";import{HeadingNode as a}from"./nodes/heading-node.js";class j{#s;#o;#i;#t;constructor({ast:i,partials:s,skipConditionals:r=!1,getInnerContent:t}){if(this.#s=i,this.#o=s,this.#i=r,this.#t=t,!this.#s||!f(this.#s))throw new Error("ast is not a valid Markdoc Node.")}*transform(){yield*this.#l(this.#s,{parentNode:null})}*#l(i,s,r=this.#t){if(!(!i||!f(i))){if(y(i)){if(this.#i)return;const t=T(i);t&&t.length>0&&(yield*this.#r(i,{...s,rbacTeams:t},r));return}if(N(i)){yield new e({node:i,content:r([i],{skipConditionals:this.#i}),...s});return}if(i.type==="heading"){yield new a({node:i,content:r([i],{skipConditionals:this.#i}),rbacTeams:s?.rbacTeams});return}if(i.type==="tag"){yield*this.#c(i,s,r);return}yield*this.#r(i,s,r)}}*#c(i,s,r=this.#t){switch(i.tag){case"partial":{const t=i.attributes.file,o=i.attributes.variables??{};if(t&&this.#o[t]){yield*this.#l(this.#o[t],s,(l,h)=>r(l,{...h,variables:o}));return}u.warn(`Could not create search indexes for partial \u201C${t}\u201D: file not found`);return}case"cards":case"tabs":case"code-walkthrough":{yield*this.#r(i,s,r);return}case"markdoc-example":{const t=r([i],{skipConditionals:this.#i});yield new e({node:i,content:t,...s});return}case"code-snippet":{const t=n(i,"title"),o=r([i],{skipConditionals:this.#i});if(t){const l=new p({node:i,content:t,...s});s={...s,parentNode:l},yield l}o&&(yield new e({node:i,content:o,...s}));return}default:{const t=b.find(c=>c in i.attributes),o=(t&&n(i,t))??"",l=typeof o=="string"?o:r([o]);if(l){const c=new p({node:i,content:l,...s});s={...s,parentNode:c},yield c}if(i.children.length>0||Object.keys(i.slots).length>0)yield*this.#r(i,s,r);else{const c=r([i],{skipConditionals:this.#i});c&&(yield new e({node:i,content:c,...s}))}return}}}*#r(i,s,r=this.#t){for(const t of[...Object.values(i.slots),...i.children])for(const o of this.#l(t,s,r))o instanceof a&&(s={...s,parentNode:o}),yield o}}export{j as AstToSearchNodeTransformer};
@@ -1 +1 @@
1
- import{simplifyAstStructure as ee}from"@redocly/openapi-docs";import{REDOCLY_TEAMS_RBAC as D}from"@redocly/config";import{OPENAPI_DOCS_TEMPLATE_ID as $,PUBLIC_RBAC_SCOPE_ITEM as j}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as te,PUBLIC_API_DEFINITIONS_FOLDER as oe}from"../../constants/common.js";import{OPENAPI_CUSTOM_FIELDS_SERVER_PROPS_GETTER_ID as ae,OPENAPI_SHARED_DATA_PREFIX as U}from"../../constants/plugins/openapi-docs.js";import{envConfig as V}from"../../config/env-config.js";import{isRbacScopeItems as re}from"../../utils/rbac/is-rbac-scope-items.js";import{getRouteSlugsForPath as se}from"../utils.js";import{searchResolver as ne}from"./search/search-resolver.js";import{convertOpenAPIDocs2Sidebar as ie,shouldAddRoute as pe}from"./utils.js";import{getTemplatePath as T}from"./get-template-path.js";import{storeDefinitionBundles as ce}from"./store-definition-bundles.js";import{definitionLoader as de,definitionsLoader as le}from"./load-definition.js";import{getAiDocumentsStore as ue}from"./search/get-ai-search-documents.js";import{fromCurrentDir as me}from"../../utils/paths.js";import{telemetryTraceStep as fe}from"../../../cli/telemetry/helpers/trace-step.js";const w="openapi-spec-download";async function Te(){let M=[],R={},L=new Set;return{id:"openapi",requiredEntitlements:["openapi"],loaders:{"load-oas-docs":le,"load-oas":de},processContent:async(e,i)=>{await fe("build.plugin.openapi_docs",async u=>{e.createRequestHandler(w,me(import.meta.url,"./spec-download.api.js")),e.addApiRoute({slug:oe+"/*",requestHandlerId:w,httpMethod:"all",[D]:j,getStaticData:async()=>({props:{}})}),e.addApiRoute({slug:te+"/*",requestHandlerId:w,httpMethod:"all",[D]:j,getStaticData:async()=>({props:{}})});const A=e.createTemplate($,T("../../../client/templates/openapi-docs/template.js")),a=e.registerServerPropsGetter($,T("./get-server-props.js")),r=e.registerServerPropsGetter(ae,T("./get-server-props-custom-fields.js")),p=await i.getConfig();u?.setAttribute("config",JSON.stringify(p.openapi||{}));const h=p.rules?.["custom-fields-schema"];R={};const m=await e.loadOpenApiDefinitions(i);M=m.map(({markdocChunks:c,relativePath:f,customOutputRelativeFile:s,isVirtual:n,realRelativePath:d})=>({chunks:c,relativePath:f,realRelativePath:d,isVirtual:s!=null||n})),ce(m,e.outdir);const S={};for(const c of m||[]){const{definition:f,config:s,relativePath:n,customOutputRelativeFile:d,contentItems:_,flatItems:k,parser:E,options:B,rawOptions:q,hash:J}=c,N=d||n,o=[],x={},{definition:Q}=E||{},{info:l}=Q||{},v=l?.["x-metadata"],C=!!s.openapi?.excludeFromSearch||!!s.theme?.openapi?.excludeFromSearch||!!p.openapi?.excludeFromSearch||!!p.theme?.openapi?.excludeFromSearch,G={title:l?.title,description:l?.description,summary:l?.summary,...s.metadata,...v},g={untagged:[],tagged:new Map};for(const t of k){const{id:O,href:I,operationDefinition:b}=t;if(b){const{tags:P}=b;if(P)for(const F of P)g.tagged.has(F)||g.tagged.set(F,[]),g.tagged.get(F)?.push(t);else g.untagged.push(t);V.isDevelopMode&&(x[`#${b.pointer}`]=t.href)}if(!pe({item:t}))continue;const y=t,Y=y.type==="section"&&!!y.infoDefinition,z=I.split("#")[0]+"/",K=t?.operationDefinition?.[D],Z=re(s.access?.rbac)?s.access.rbac:void 0;o.push({excludeFromSearch:C,slugSuffix:z,fsPath:N,metadata:{subType:"openapi-operation"},httpVerb:t?.httpVerb||"",path:n,templateId:A,[D]:K||Z,getAiDocumentsStore:ue({parser:E,options:B,info:l,tagOperations:g,openapiContentItem:y,metadata:G,relativePath:n,getSearchFacets:e.getSearchFacets,includeInLLMsTxt:Y,excludeFromSearch:C}),getStaticData:async P=>({props:{dynamicMarkdocComponents:["openapi"],baseSlug:P.baseSlug,seo:t["x-metadata"]?.seo||{title:t.name,description:t.description},itemId:O,disableAutoScroll:!0}})})}o[0]={...o[0],metadata:{type:"openapi",...G},hasClientRoutes:!0,getSidebar:(t,O)=>{const I=[];return ie({contentItems:_,sidebarItems:I,routeSlug:t.slug,navItem:O}),I},getNavText:()=>l?.title,getSearchDocuments:ne(E,B,k,e.getSearchFacets,e.setSearchFacets,C)},v?.apiId&&(S[v.apiId]={slug:o[0]?.slug||""});const W=o[0];o[0]=o[o.length-1],o[o.length-1]=W;for(const t of o)e.addRoute({...t,serverPropsGetterIds:h?[a,r]:[a]});const X=V.isDevelopMode?n:void 0,H=`${U}${n}`;R[H]={fsPath:N,definition:f,options:q,sourcePath:X,routesMapping:x,hash:J};for(const t of o)e.addRoute({...t,sharedData:[{id:H,key:"openAPIDocsStore"}],serverPropsGetterIds:h?[a,r]:[a]})}e.setGlobalData({apiProducts:S})})},afterRoutesCreated:async(e,i)=>{const u=new Set;for(const{chunks:a,relativePath:r,isVirtual:p,realRelativePath:h}of M){const m=se(e.getAllRoutes(),r),S=(await i.cache.load(h,"load-oas")).compoundHash;await i.cache.load(r,{loader:async function(){for(const{node:f,markdown:s,key:n,relativePath:d}of a){const{ast:_}=await e.parseMarkdoc({input:{content:s,relativePath:d,isVirtual:p},context:i,deps:{sharedDataIds:[`${U}${d}`],routeSlugs:m}});f[`x-parsed-md-${n}`]={result:ee(_)}}},name:"openapi-markdoc-inline-parser"},[S]);for(const{pointer:c}of a)u.add(c)}const A=L.difference(u);for(const a of A)i.cache.delete(a);L=u;for(const[a,r]of Object.entries(R))await e.createSharedData(a,{...r,baseSlug:e.getRouteByFsPath(r.fsPath)?.baseSlug},r.hash)}}}export{Te as openAPIDocsPlugin};
1
+ import{simplifyAstStructure as te}from"@redocly/openapi-docs";import{REDOCLY_ROUTE_RBAC as E,REDOCLY_TEAMS_RBAC as P}from"@redocly/config";import{OPENAPI_DOCS_TEMPLATE_ID as U,PUBLIC_RBAC_SCOPE_ITEM as V}from"../../../constants/common.js";import{DEPRECATED_PUBLIC_API_DEFINITIONS_FOLDER as oe,PUBLIC_API_DEFINITIONS_FOLDER as ae}from"../../constants/common.js";import{OPENAPI_CUSTOM_FIELDS_SERVER_PROPS_GETTER_ID as re,OPENAPI_SHARED_DATA_PREFIX as $}from"../../constants/plugins/openapi-docs.js";import{envConfig as q}from"../../config/env-config.js";import{isRbacScopeItems as se}from"../../utils/rbac/is-rbac-scope-items.js";import{getRouteSlugsForPath as ie}from"../utils.js";import{searchResolver as ne}from"./search/search-resolver.js";import{convertOpenAPIDocs2Sidebar as pe,shouldAddRoute as ce}from"./utils.js";import{getTemplatePath as M}from"./get-template-path.js";import{storeDefinitionBundles as de}from"./store-definition-bundles.js";import{definitionLoader as le,definitionsLoader as ue}from"./load-definition.js";import{getAiDocumentsStore as fe}from"./search/get-ai-search-documents.js";import{fromCurrentDir as me}from"../../utils/paths.js";import{telemetryTraceStep as ge}from"../../../cli/telemetry/helpers/trace-step.js";import{getApiConfigByPath as he}from"../get-api-config.js";const B="openapi-spec-download";async function Me(){let k=[],_={},N=new Set,f={};return{id:"openapi",requiredEntitlements:["openapi"],loaders:{"load-oas-docs":ue,"load-oas":le},processContent:async(e,c)=>{await ge("build.plugin.openapi_docs",async m=>{e.createRequestHandler(B,me(import.meta.url,"./spec-download.api.js")),e.addApiRoute({slug:ae+"/*",requestHandlerId:B,httpMethod:"all",[P]:V,getStaticData:async()=>({props:{}})}),e.addApiRoute({slug:oe+"/*",requestHandlerId:B,httpMethod:"all",[P]:V,getStaticData:async()=>({props:{}})});const C=e.createTemplate(U,M("../../../client/templates/openapi-docs/template.js")),o=e.registerServerPropsGetter(U,M("./get-server-props.js")),a=e.registerServerPropsGetter(re,M("./get-server-props-custom-fields.js")),s=await c.getConfig();m?.setAttribute("config",JSON.stringify(s.openapi||{}));const d=s.rules?.["custom-fields-schema"];_={},f={};const g=await e.loadOpenApiDefinitions(c);k=g.map(({markdocChunks:p,relativePath:l,customOutputRelativeFile:i,isVirtual:n,realRelativePath:h})=>({chunks:p,relativePath:l,realRelativePath:h,isVirtual:i!=null||n})),de(g,e.outdir);for(const p of g||[]){const{definition:l,config:i,relativePath:n,customOutputRelativeFile:h,contentItems:I,flatItems:D,parser:v,options:x,rawOptions:Y,hash:J}=p,O=h||n,r=[],G={},{definition:Q}=v||{},{info:u}=Q||{},b=u?.["x-metadata"],y=!!i.openapi?.excludeFromSearch||!!i.theme?.openapi?.excludeFromSearch||!!s.openapi?.excludeFromSearch||!!s.theme?.openapi?.excludeFromSearch,j={title:u?.title,description:u?.description,summary:u?.summary,...i.metadata,...b},S={untagged:[],tagged:new Map};for(const t of D){const{id:F,href:R,operationDefinition:T}=t;if(T){const{tags:A}=T;if(A)for(const L of A)S.tagged.has(L)||S.tagged.set(L,[]),S.tagged.get(L)?.push(t);else S.untagged.push(t);q.isDevelopMode&&(G[`#${T.pointer}`]=t.href)}if(!ce({item:t}))continue;const w=t,z=w.type==="section"&&!!w.infoDefinition,K=R.split("#")[0]+"/",Z=t?.operationDefinition?.[P],ee=se(i.access?.rbac)?i.access.rbac:void 0;r.push({excludeFromSearch:y,slugSuffix:K,fsPath:O,metadata:{subType:"openapi-operation"},httpVerb:t?.httpVerb||"",path:n,templateId:C,[P]:Z||ee,getAiDocumentsStore:fe({parser:v,options:x,info:u,tagOperations:S,openapiContentItem:w,metadata:j,relativePath:n,getSearchFacets:e.getSearchFacets,includeInLLMsTxt:z,excludeFromSearch:y}),getStaticData:async A=>({props:{dynamicMarkdocComponents:["openapi"],baseSlug:A.baseSlug,seo:t["x-metadata"]?.seo||{title:t.name,description:t.description},itemId:F,disableAutoScroll:!0}})})}if(r[0]={...r[0],metadata:{type:"openapi",...j},hasClientRoutes:!0,getSidebar:(t,F)=>{const R=[];return pe({contentItems:I,sidebarItems:R,routeSlug:t.slug,navItem:F}),R},getNavText:()=>u?.title,getSearchDocuments:ne(v,x,D,e.getSearchFacets,e.setSearchFacets,y)},b?.apiId&&!p.isVirtual){const t=he(p.config.apis,p.realRelativePath);f[b.apiId]={[P]:t.rbac,[E]:{fsPath:O}}}const W=r[0];r[0]=r[r.length-1],r[r.length-1]=W;for(const t of r)e.addRoute({...t,serverPropsGetterIds:d?[o,a]:[o]});const X=q.isDevelopMode?n:void 0,H=`${$}${n}`;_[H]={fsPath:O,definition:l,options:Y,sourcePath:X,routesMapping:G,hash:J};for(const t of r)e.addRoute({...t,sharedData:[{id:H,key:"openAPIDocsStore"}],serverPropsGetterIds:d?[o,a]:[o]})}})},afterRoutesCreated:async(e,c)=>{const m=new Set;for(const{chunks:o,relativePath:a,isVirtual:s,realRelativePath:d}of k){const g=ie(e.getAllRoutes(),a),p=(await c.cache.load(d,"load-oas")).compoundHash;await c.cache.load(a,{loader:async function(){for(const{node:i,markdown:n,key:h,relativePath:I}of o){const{ast:D}=await e.parseMarkdoc({input:{content:n,relativePath:I,isVirtual:s},context:c,deps:{sharedDataIds:[`${$}${I}`],routeSlugs:g}});i[`x-parsed-md-${h}`]={result:te(D)}}},name:"openapi-markdoc-inline-parser"},[p]);for(const{pointer:l}of o)m.add(l)}const C=N.difference(m);for(const o of C)c.cache.delete(o);N=m;for(const[o,a]of Object.entries(_))await e.createSharedData(o,{...a,baseSlug:e.getRouteByFsPath(a.fsPath)?.baseSlug},a.hash);Object.entries(f).forEach(([o,a])=>{const s=a[E]?.fsPath;if(!s)return;const d=e.getRouteByFsPath(s)?.baseSlug;f[o]={...a,[E]:{...a[E],slug:d},slug:d}}),e.setGlobalData({apiProducts:f})}}}export{Me as openAPIDocsPlugin};
@@ -1 +1 @@
1
- import*as y from"@redocly/openapi-docs";import{REDOCLY_TEAMS_RBAC as d}from"@redocly/config";import{combineUrls as u}from"@redocly/theme/core/utils";import{SEARCH_DOCUMENT_METADATA_KEY as x}from"../../constants/plugins/search.js";import{removeMarkdownLinks as c,stripFormatting as a}from"./utils.js";import{normalizeFrontmatterKeywords as k}from"../helpers/normalize-frontmatter-keywords.js";const h=y.default||y;class q{#n=[];#r;#s;#e;#i;constructor(e,t,n){this.#r=e,this.#s=t,this.#e=n}addItem(e){const{result:t}=k(e.keywords||e.operationDefinition?.keywords);t?.excludes&&e.type==="section"&&e.id===""&&(this.#i=t.excludes);try{let n;switch(e.type){case"tag":n=this.#c(e);break;case"operation":const o=this.getOperation(e);n=this.#d(o,e[d]);break;case"section":n=this.#p(e);break;case"rsrc":case"prompt":case"tool":n=this.#o(e);break}if(!n)return;const s=[...new Set([...t?.excludes?t.excludes:[],...this.#i?this.#i:[]])];return(t||s.length)&&(n[x]={curated:!0,...t,excludes:s}),this.#n.push(n),n}catch(n){console.error("Cannot add item to search indexer",n.message)}}addInfo(e,t){const n=this.#e,s={id:n,url:n,text:a(c(e.description||"")),title:a(`${e.title} (${e.version})`),metadata:t};return this.#n.push(s),s}#o(e){const t=u(this.#e,e.href);return{id:t,url:t,text:a(c(e.description||"")),title:e.name}}getResult(){return this.#n}getOperation(e){return h.getOperation(this.#r,e.operationDefinition,e.parent,{...this.#s,internal_skipSamples:!0},e.href)}#a(e){return[...e.path||[],e.name.toString()].join(".")+e.description+e.place}#d(e,t){if(e.type!=="operation")return;let n={};for(let i of e.parameters){if(i[d])continue;const r=i.schema?.example||i.example,p={name:i.name,description:a(c(i.description)),place:i.in+" parameters",mediaType:void 0,type:i.schema?.type.toString()||"unknown",deepLink:h.generateDeepLink(i),[d]:i[d],required:i.required,example:r?JSON.stringify(r):void 0,enum:i.schema?.enum?.length?i.schema.enum:void 0};n[this.#a(p)]=p}const s=new Set;this.#t(n,e.requestBody?.content?.mediaTypes[0]?.schema,e.requestBody?.content?.mediaTypes[0]?.name,"request fields",!1,[],s);for(let i of e.responses){const r=`response ${i.code} fields`;this.#t(n,i.content?.mediaTypes[0]?.schema,i.content?.mediaTypes[0]?.name,r,!0,[],s)}let o=u(this.#e,e.href);return{id:o,url:o,title:a(e.name),text:a(c(e.description||"")),httpMethod:e.httpVerb,httpPath:e.path,deprecated:e.deprecated,isAdditionalOperation:e.isAdditionalOperation,security:e.security.map(i=>i.schemes.map(r=>r.id)).flat().filter(Boolean),parameters:Object.values(n),badges:e.badges.length?e.badges:void 0,[d]:t}}#t(e,t,n,s,o,l=[],i=new Set){if(!(!t||t.isCircular)){if(t.pointer){if(i.has(t.pointer))return;i.add(t.pointer)}if(t?.fields)for(let r of t.fields){if(t[d]||r.kind==="additionalProperties"||r.schema?.readOnly&&!o||r.schema?.writeOnly&&o)continue;const p=r.schema?.example||r.example,m=r.schema?.enum,f={name:r.name,description:a(r.description),place:s,mediaType:n,path:l,deepLink:h.generateDeepLink(r),type:r.schema?.type.toString()||"unknown",required:r.required||t.schema.required?.includes(r.name)||!1,example:p?JSON.stringify(p):void 0,enum:m?.length?m:void 0},g=this.#a(f);e[g]==null&&(e[g]=f,this.#t(e,r.schema,n,s,o,l.concat([r.name]),i))}t?.items&&this.#t(e,t.items,n,s,o,l,i)}}#c(e){const t=u(this.#e,e.href);return{id:t,url:t,text:a(c(e.description||"")),title:a(e.name)}}#p(e){const t=u(this.#e,e.href);return{id:t,url:t,text:a(c(e.description||"")),title:a(e.name)}}}export{q as SearchIndexer};
1
+ import*as y from"@redocly/openapi-docs";import{REDOCLY_TEAMS_RBAC as c}from"@redocly/config";import{combineUrls as l}from"@redocly/theme/core/utils";import{SEARCH_DOCUMENT_METADATA_KEY as k}from"../../constants/plugins/search.js";import{removeMarkdownLinks as p,stripFormatting as a}from"./utils.js";import{normalizeFrontmatterKeywords as O}from"../helpers/normalize-frontmatter-keywords.js";const h=y.default||y;class q{#n=[];#s;#r;#e;#i;constructor(e,t,n){this.#s=e,this.#r=t,this.#e=n}addItem(e){const{result:t}=O(e.keywords||e.operationDefinition?.keywords);t?.excludes&&e.type==="section"&&e.id===""&&(this.#i=t.excludes);try{let n;switch(e.type){case"tag":n=this.#c(e);break;case"operation":const d=this.getOperation(e);n=this.#d(d,e[c]);break;case"section":n=this.#p(e);break;case"rsrc":case"prompt":case"tool":n=this.#o(e);break}if(!n)return;const s=[...new Set([...t?.excludes?t.excludes:[],...this.#i?this.#i:[]])];return(t||s.length)&&(n[k]={curated:!0,...t,excludes:s}),this.#n.push(n),n}catch(n){console.error("Cannot add item to search indexer",n.message)}}addInfo(e,t){const n=this.#e,s={id:n,url:n,text:a(p(e.description||"")),title:a(`${e.title} (${e.version})`),metadata:t};return this.#n.push(s),s}#o(e){const t=l(this.#e,e.href);return{id:t,url:t,text:a(p(e.description||"")),title:e.name}}getResult(){return this.#n}getOperation(e){return h.getOperation(this.#s,e.operationDefinition,e.parent,{...this.#r,internal_skipSamples:!0},e.href)}#a(e){return[...e.path||[],e.name.toString()].join(".")+e.description+e.place}#d(e,t){if(e.type!=="operation")return;let n={};for(let r of e.parameters){if(r[c])continue;const o=r.schema?.example||r.example,i={name:r.name,description:a(p(r.description)),place:r.in+" parameters",mediaType:void 0,type:r.schema?.type.toString()||"unknown",deepLink:h.generateDeepLink(r),[c]:r[c],required:r.required,example:o?JSON.stringify(o):void 0,enum:r.schema?.enum?.length?r.schema.enum:void 0};n[this.#a(i)]=i}const s=new Set;this.#t(n,e.requestBody?.content?.mediaTypes[0]?.schema,e.requestBody?.content?.mediaTypes[0]?.name,"request fields",!1,[],s);for(let r of e.responses){const o=`response ${r.code} fields`;this.#t(n,r.content?.mediaTypes[0]?.schema,r.content?.mediaTypes[0]?.name,o,!0,[],s)}let d=l(this.#e,e.href);return{id:d,url:d,title:a(e.name),text:a(p(e.description||"")),httpMethod:e.httpVerb,httpPath:e.path,deprecated:e.deprecated,isAdditionalOperation:e.isAdditionalOperation,security:e.security.map(r=>r.schemes.map(o=>o.id)).flat().filter(Boolean),parameters:Object.values(n),badges:e.badges.length?e.badges:void 0,[c]:t}}#t(e,t,n,s,d,u=[],r=new Set,o=0){if(!(!t||t.isCircular)){if(t.pointer){if(r.has(t.pointer))return;r.add(t.pointer)}if(!(o>this.#r.generatedSamplesMaxDepth)){if(t?.fields)for(let i of t.fields){if(t[c]||i.kind==="additionalProperties"||i.schema?.readOnly&&!d||i.schema?.writeOnly&&d)continue;const m=i.schema?.example||i.example,f=i.schema?.enum,g={name:i.name,description:a(i.description),place:s,mediaType:n,path:u,deepLink:h.generateDeepLink(i),type:i.schema?.type.toString()||"unknown",required:i.required||t.schema.required?.includes(i.name)||!1,example:m?JSON.stringify(m):void 0,enum:f?.length?f:void 0},x=this.#a(g);e[x]==null&&(e[x]=g,this.#t(e,i.schema,n,s,d,u.concat([i.name]),r,o+1))}t?.items&&this.#t(e,t.items,n,s,d,u,r,o+1)}}}#c(e){const t=l(this.#e,e.href);return{id:t,url:t,text:a(p(e.description||"")),title:a(e.name)}}#p(e){const t=l(this.#e,e.href);return{id:t,url:t,text:a(p(e.description||"")),title:a(e.name)}}}export{q as SearchIndexer};
@@ -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 } from './types';
4
+ import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, RedirectOptions } from './types';
5
5
  import type { RedoclyConfig, RedirectConfig, CompilationError, PageStaticData } from '@redocly/config';
6
6
  import type { SearchFacet } from '@redocly/theme/core/types';
7
7
  import type { BundledDefinition as OpenApiBundledDefinition } from './plugins/openapi-docs/load-definition.js';
@@ -140,7 +140,7 @@ export declare class Store {
140
140
  getConfig: () => RedoclyConfig;
141
141
  getGlobalConfig: <T = unknown>(key: string) => T;
142
142
  getSearchFacets: () => Map<string, SearchFacet>;
143
- addRedirect: (from: string, to: RedirectConfig) => void;
143
+ addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
144
144
  getRedirect: (from: string) => WithRequired<RedirectConfig, "to"> | null;
145
145
  createSharedData: (id: string, data: unknown, hash?: string) => Promise<string>;
146
146
  addRouteSharedData: (routeSlug: string, dataKey: string, dataId: string) => void;
@@ -1 +1 @@
1
- import b from"@markdoc/markdoc";import{getPathnameForLocale as A}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as f}from"../constants/common.js";import{DEFAULT_TITLE as C}from"./constants/common.js";import{GATED_MARKDOC_TAGS as D}from"./constants/entitlements.js";import{isObject as O}from"../utils/guards/is-object.js";import{mapObject as T}from"../utils/object/map-object.js";import{getValueDeep as S}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as M}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as p}from"../utils/path/normalize-route-slug.js";import{isLocalLink as L}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as g}from"./tools/notifiers/logger.js";import{sha1 as k}from"./utils/crypto/sha1.js";import{writeEnvVariable as _}from"./utils/envs/write-env-variable.js";import{envConfig as F}from"./config/env-config.js";import{KvService as G}from"./persistence/kv/services/kv-service.js";import{writeSharedData as B}from"./utils/index.js";import{renderComponents as I}from"./ssr/render.js";import{readStaticData as N,writeStaticData as V}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as H}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as y}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveMetadataGlobs as U}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as q}from"./utils/redirects/find-redirect.js";import{addWildcardRedirectToTree as x}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as $}from"../cli/telemetry/helpers/trace-step.js";const R={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map"},m="markdown/partials",W="markdown/partials-deps",v="PLAN_GATES",z=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],At="userDefinedApiFunctions";class E{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;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#s=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#r;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#a;userCodeReady;#o=Promise.resolve();#i;#n=Promise.resolve();#c;#e=new Map;constructor({outdir:t,contentDir:e,serverMode:s=!1,serverOutDir:a}){this.#r=e,this.outdir=t,this.serverMode=s,this.serverOutDir=a,this.userCodeReady=new Promise(r=>{this.#a=r})}on(t,e){const s=this.listeners.get(t);s?s.add(e):this.listeners.set(t,new Set([e]))}queueEvent=(t,e,...s)=>{this.#e.set(t+String(e),[t,e,...s])};runListeners=(t,e,...s)=>{for(const a of this.listeners.get(t)||new Set)e?a(e,...s):a(...s)};startPluginsRun(){this.clear(),this.#o=new Promise(t=>{this.#i=t})}waitForPluginsLifecycle(){return Promise.all([this.#o,this.#n])}finishPluginsRun(){this.#i?.();for(const t of this.#e.values())this.runListeners(...t);this.#e.clear()}startEsbuildRun(){this.#n=new Promise(t=>{this.#c=t})}finishEsbuildRun(){this.#c?.()}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#r}markUserCodeReady(){this.#a?.(!0)}async reloadMarkdocOptions(){await $("build.reload_markdoc_options",async()=>{const t=y.instance(),e=await H(this.serverOutDir),s=Object.fromEntries(Object.entries(e.tags).filter(([a])=>D[a]!=null?t.canAccessFeature(D[a]):!0));this.#s={...e,tags:s}})}get markdocOptions(){return{...this.#s,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=t=>{const e=this.globalData,s={...this.globalData,...t};this.globalData=s,JSON.stringify(s)!==JSON.stringify(e)&&this.queueEvent("global-data-updated",void 0,s)};getGlobalData=()=>this.globalData;getKv=async()=>G.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:t,context:e,deps:s,resource:a})=>{const{data:{info:r,ast:c},compoundHash:l}=await j(t,this.markdocOptions,{actions:this,context:e},a);for(const i of r.sharedDataDeps||[]){for(const o of s?.routeSlugs||[])this.addRouteSharedData(o,i,i);for(const o of s?.sharedDataIds||[]){const n=this.sharedDataDeps.get(o)||new Set;n.add(i),this.sharedDataDeps.set(o,n)}}for(const i of r.dynamicMarkdocComponents||[]){for(const o of s?.routeSlugs||[]){const n=this.routesDynamicComponents.get(o)||new Set;n.add(i),this.routesDynamicComponents.set(o,n)}for(const o of s?.sharedDataIds||[]){const n=this.sharedDataMarkdocComponents.get(o)||new Set;n.add(i),this.sharedDataMarkdocComponents.set(o,n)}}if(s?.routeSlugs&&r.partials?.length)for(const i of s.routeSlugs){const o=this.routesPartials.get(i)||[];for(const n of r.partials)o.includes(n)||o.push(n);this.routesPartials.set(i,o)}return{info:r,ast:c,compoundHash:l}};async loadOpenApiDefinitions(t){return(await t.cache.load(".","load-oas-docs")).data}async loadAsyncApiDefinitions(t){return(await t.cache.load(".","asyncapi-docs")).data}setSearchEngine(t){this.searchEngine=t}setSearchFacets=t=>{this.searchFacets=t};setGlobalConfig=t=>{const e=Object.keys(t);for(const c of e)for(const l in this.replacedEnvVars)if(l===c||l.startsWith(`${c}:`)){const i=l.split(":"),{error:o,value:n}=S(t,i);(o||n!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:s,unsetEnvVars:a,replacedValues:r}=J(t);for(const c of a)this.unsetEnvVars.add(c);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,s)};getConfig=()=>this.config;getGlobalConfig=t=>this.config[t];getSearchFacets=()=>this.searchFacets;addRedirect=(t,e)=>{if(!y.instance().canAccessFeature("redirects")&&t!=="/")return;this.config.redirects||(this.config.redirects={});const r=p(t).toLowerCase();this.config.redirects[r]=e,r.endsWith("*")&&x(this.config.wildcardRedirectsTree,r)};getRedirect=t=>{const e=p(t).toLowerCase();return q(e,this.config.redirects,this.config.wildcardRedirectsTree)};createSharedData=async(t,e,s)=>{if(s&&this.#t[t]===s)return t;const a=JSON.stringify(e),r=s??k(a);return this.#t[t]===r||(this.#t[t]=r,await B(t,a,this.outdir),this.queueEvent("shared-data-updated",t)),t};addRouteSharedData=(t,e,s)=>{const a=M(t),r=this.routesSharedData.get(a)||{};r[e]=s,this.routesSharedData.set(a,r),g.verbose(`Adding shared data to ${t}, ${e}, ${s}`)};getRouteSharedDataByFsPath=t=>{const e=this.routesByFsPath.get(t);return e?this.routesSharedData.get(e)||{}:{}};getPartialsForRoute=t=>{const e=this.getGlobalConfig(m)||{},s=this.routesPartials.get(t);if(!s||s.length===0)return{};const a=this.getGlobalConfig(W)||{},r=new Set(s),c=Array.from(s);for(let i=0;i<c.length;i++){const o=c[i],n=a[o]?.partials??[];for(const d of n)r.has(d)||(r.add(d),c.push(d))}const l={};for(const i of r)e[i]&&(l[i]=e[i]);return l};addRoute=t=>{const s={...U(t.fsPath,this.config.metadataGlobs),...t.metadata||{}};this.newRoutes.push({...t,metadata:s}),g.verbose("Created route %s",t.slug)};addRouteSharedDataToAllLocales=(t,e,s)=>{const a=[f,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const c=A(t,f,r.code,a);this.addRouteSharedData(c,e,s)}};addApiRoute=t=>{this.apiRoutes.push(t),g.verbose("Created API route %s",t.slug)};addMiddleware=t=>{this.middleware.push(t),g.verbose("Created middleware %s",t.id)};getRouteByFsPath=t=>{const e=this.routesByFsPath.get(t);return e?this.getRouteBySlug(e):void 0};getRouteBySlug=(t,e={})=>{const{followRedirect:s=!0}=e,a=this.getRedirect(t);return s&&a?this.routesBySlug.get(p(a.to)):this.routesBySlug.get(t)};slugHasRouteOrRedirect=t=>{if(this.routesBySlug.has(t))return!0;const e=this.getRedirect(t);if(!e)return!1;if(!L(e.to))return!0;const s=p(e.to);return this.routesBySlug.has(s)};getRoutesByTemplateId=t=>this.newRoutes.filter(e=>e.templateId===t);getAllRoutesForLocale=(t=f)=>{const e=Array.from(this.routesBySlug.values()),s=t.toLowerCase();return e.filter(a=>t===f?!K(a.fsPath):a.slug.startsWith(`/${s}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=t=>this.templates.get(t);getRequestHandler=t=>this.apiRoutesRequestHandlers.get(t);createTemplate=(t,e)=>(this.templates.set(t,e),t);addBrowserPlugin=t=>{this.browserPlugins.add(t)};createRequestHandler=(t,e)=>(this.apiRoutesRequestHandlers.set(t,e),t);registerServerPropsGetter=(t,e)=>(this.serverPropsGetters.set(t,e),t);registerPagePropsGetter=(t,e)=>{this.pagePropsGetters.set(t,e)};async writeRouteStaticData(t,e){const s=await this.resolveRouteStaticData(t,e,!1);s&&V(t.slug,s,this.outdir)}async resolveRouteStaticData(t,e,s){if(this.serverMode)return N(t.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:n,context:d,resource:h})=>this.parseMarkdoc({input:n,context:d,deps:{routeSlugs:[t.slug]},resource:h})},r=await t.getStaticData?.(t,a)||{},c=new Set(this.routesDynamicComponents.get(t.slug)),l=this.routesSharedData.get(t.slug)||{};for(const n of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(n);d&&d.forEach(u=>c.add(u));const h=this.sharedDataDeps.get(n);h&&h.forEach(u=>this.addRouteSharedData(t.slug,u,u))}const i=this.getGlobalConfig("seo"),o=r?.frontmatter||{};return{...r,frontmatter:{...o,seo:{...o?.seo,title:o?.seo?.title||await t.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(c),metadata:{...r?.props?.metadata,...t.metadata},seo:{title:C,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:s||!t.fsPath?null:await this.lifecycleContext?.fs.getLastModified(t.fsPath)}}addSsrComponents(t,e){if(!t?.length)return;const s=typeof t[0]=="string"?t.join(""):I(t);s&&(e==="head"?this.ssr.headTags.push(s):e==="preBody"?this.ssr.preBodyTags.push(s):this.ssr.postBodyTags.push(s))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const t=[];for(const[s,a]of Object.entries(R))switch(a){case"map":const r=Array.from(this[s].entries());t.push([s,r]);break;case"object":s==="config"&&t.push([s,await this.getConfigWithEnvPlaceholders()]),t.push([s,this[s]]);break;default:throw new Error("Invalid format")}const e=Object.fromEntries(t);return e[v]=F.PLAN_GATES,e}static fromJson(t,e){const s=new E(e);for(const[r,c]of Object.entries(R))switch(c){case"map":s[r]=new Map(t[r]);break;case"object":if(r==="config"){s.setGlobalConfig(t[r]);break}s[r]=t[r];break;default:throw new Error("Invalid format")}s.config[m]=Y(s.config[m]||{});const a=t[v];return a&&_("PLAN_GATES",a),s}async getConfigWithEnvPlaceholders(){const t=JSON.parse(JSON.stringify(this.config));for(const e in this.replacedEnvVars){const{original:s}=this.replacedEnvVars[e],a=e.split(":"),r=a.pop(),{error:c,value:l}=S(t,a);if(c||!O(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${e}`);continue}l[r]=s}return t}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const t=Array.from(this.unsetEnvVars).filter(s=>!z.includes(s));if(t.length===0)return;const e=`Failed to resolve config. The following environment variables are not set: ${t.join(", ")}`;await w.panicOnBuildContentError(e)}}function Y(P){return T(P,t=>b.Ast.fromJSON(JSON.stringify(t)))}export{m as MARKDOC_PARTIALS_DATA_KEY,W as MARKDOC_PARTIALS_DEPS_KEY,E as Store,At as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
1
+ import A from"@markdoc/markdoc";import{getPathnameForLocale as C}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as p}from"../constants/common.js";import{DEFAULT_TITLE as O}from"./constants/common.js";import{GATED_MARKDOC_TAGS as D}from"./constants/entitlements.js";import{isObject as T}from"../utils/guards/is-object.js";import{mapObject as M}from"../utils/object/map-object.js";import{getValueDeep as S}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as y}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as g}from"./tools/notifiers/logger.js";import{sha1 as k}from"./utils/crypto/sha1.js";import{writeEnvVariable as _}from"./utils/envs/write-env-variable.js";import{envConfig as G}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{writeSharedData as B}from"./utils/index.js";import{renderComponents as I}from"./ssr/render.js";import{readStaticData as N,writeStaticData as V}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as H}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as R}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveMetadataGlobs as U}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as q}from"./utils/redirects/find-redirect.js";import{followRedirectChain as x}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as W}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as $}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map"},m="markdown/partials",z="markdown/partials-deps",E="PLAN_GATES",Y=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ot="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#s=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#r;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#a;userCodeReady;#o=Promise.resolve();#i;#n=Promise.resolve();#c;#e=new Map;constructor({outdir:t,contentDir:s,serverMode:e=!1,serverOutDir:a}){this.#r=s,this.outdir=t,this.serverMode=e,this.serverOutDir=a,this.userCodeReady=new Promise(r=>{this.#a=r})}on(t,s){const e=this.listeners.get(t);e?e.add(s):this.listeners.set(t,new Set([s]))}queueEvent=(t,s,...e)=>{this.#e.set(t+String(s),[t,s,...e])};runListeners=(t,s,...e)=>{for(const a of this.listeners.get(t)||new Set)s?a(s,...e):a(...e)};startPluginsRun(){this.clear(),this.#o=new Promise(t=>{this.#i=t})}waitForPluginsLifecycle(){return Promise.all([this.#o,this.#n])}finishPluginsRun(){this.#i?.();for(const t of this.#e.values())this.runListeners(...t);this.#e.clear()}startEsbuildRun(){this.#n=new Promise(t=>{this.#c=t})}finishEsbuildRun(){this.#c?.()}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#r}markUserCodeReady(){this.#a?.(!0)}async reloadMarkdocOptions(){await $("build.reload_markdoc_options",async()=>{const t=R.instance(),s=await H(this.serverOutDir),e=Object.fromEntries(Object.entries(s.tags).filter(([a])=>D[a]!=null?t.canAccessFeature(D[a]):!0));this.#s={...s,tags:e}})}get markdocOptions(){return{...this.#s,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=t=>{const s=this.globalData,e={...this.globalData,...t};this.globalData=e,JSON.stringify(e)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,e)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:t,context:s,deps:e,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(t,this.markdocOptions,{actions:this,context:s},a);for(const i of r.sharedDataDeps||[]){for(const n of e?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of e?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of e?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of e?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(e?.routeSlugs&&r.partials?.length)for(const i of e.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadOpenApiDefinitions(t){return(await t.cache.load(".","load-oas-docs")).data}async loadAsyncApiDefinitions(t){return(await t.cache.load(".","asyncapi-docs")).data}setSearchEngine(t){this.searchEngine=t}setSearchFacets=t=>{this.searchFacets=t};setGlobalConfig=t=>{const s=Object.keys(t);for(const o of s)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=S(t,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:e,unsetEnvVars:a,replacedValues:r}=J(t);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,e)};getConfig=()=>this.config;getGlobalConfig=t=>this.config[t];getSearchFacets=()=>this.searchFacets;addRedirect=(t,s,e={})=>{if(!R.instance().canAccessFeature("redirects")&&t!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(t).toLowerCase();this.config.redirects[o]=s;const{trackOriginalSource:l=!0}=e,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&W(this.config.wildcardRedirectsTree,o)};getRedirect=t=>{const s=h(t).toLowerCase(),e=q(s,this.config.redirects,this.config.wildcardRedirectsTree);if(!e)return null;if(y(e.to)){const a=h(e.to).toLowerCase();if(!a.endsWith("*")&&x(a,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:e.to,type:e.type}};createSharedData=async(t,s,e)=>{if(e&&this.#t[t]===e)return t;const a=JSON.stringify(s),r=e??k(a);return this.#t[t]===r||(this.#t[t]=r,await B(t,a,this.outdir),this.queueEvent("shared-data-updated",t)),t};addRouteSharedData=(t,s,e)=>{const a=L(t),r=this.routesSharedData.get(a)||{};r[s]=e,this.routesSharedData.set(a,r),g.verbose(`Adding shared data to ${t}, ${s}, ${e}`)};getRouteSharedDataByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=t=>{const s=this.getGlobalConfig(m)||{},e=this.routesPartials.get(t);if(!e||e.length===0)return{};const a=this.getGlobalConfig(z)||{},r=new Set(e),o=Array.from(e);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)s[i]&&(l[i]=s[i]);return l};addRoute=t=>{const e={...U(t.fsPath,this.config.metadataGlobs),...t.metadata||{}};this.newRoutes.push({...t,metadata:e}),g.verbose("Created route %s",t.slug)};addRouteSharedDataToAllLocales=(t,s,e)=>{const a=[p,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=C(t,p,r.code,a);this.addRouteSharedData(o,s,e)}};addApiRoute=t=>{this.apiRoutes.push(t),g.verbose("Created API route %s",t.slug)};addMiddleware=t=>{this.middleware.push(t),g.verbose("Created middleware %s",t.id)};getRouteByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(t,s={})=>{const{followRedirect:e=!0}=s,a=this.getRedirect(t);return e&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(t)};slugHasRouteOrRedirect=t=>{if(this.routesBySlug.has(t))return!0;const s=this.getRedirect(t);if(!s)return!1;if(!y(s.to))return!0;const e=h(s.to);return this.routesBySlug.has(e)};getRoutesByTemplateId=t=>this.newRoutes.filter(s=>s.templateId===t);getAllRoutesForLocale=(t=p)=>{const s=Array.from(this.routesBySlug.values()),e=t.toLowerCase();return s.filter(a=>t===p?!K(a.fsPath):a.slug.startsWith(`/${e}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=t=>this.templates.get(t);getRequestHandler=t=>this.apiRoutesRequestHandlers.get(t);createTemplate=(t,s)=>(this.templates.set(t,s),t);addBrowserPlugin=t=>{this.browserPlugins.add(t)};createRequestHandler=(t,s)=>(this.apiRoutesRequestHandlers.set(t,s),t);registerServerPropsGetter=(t,s)=>(this.serverPropsGetters.set(t,s),t);registerPagePropsGetter=(t,s)=>{this.pagePropsGetters.set(t,s)};async writeRouteStaticData(t,s){const e=await this.resolveRouteStaticData(t,s,!1);e&&V(t.slug,e,this.outdir)}async resolveRouteStaticData(t,s,e){if(this.serverMode)return N(t.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:u})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[t.slug]},resource:u})},r=await t.getStaticData?.(t,a)||{},o=new Set(this.routesDynamicComponents.get(t.slug)),l=this.routesSharedData.get(t.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(f=>o.add(f));const u=this.sharedDataDeps.get(c);u&&u.forEach(f=>this.addRouteSharedData(t.slug,f,f))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await t.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...t.metadata},seo:{title:O,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:e||!t.fsPath?null:await this.lifecycleContext?.fs.getLastModified(t.fsPath)}}addSsrComponents(t,s){if(!t?.length)return;const e=typeof t[0]=="string"?t.join(""):I(t);e&&(s==="head"?this.ssr.headTags.push(e):s==="preBody"?this.ssr.preBodyTags.push(e):this.ssr.postBodyTags.push(e))}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.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const t=[];for(const[e,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[e].entries());t.push([e,r]);break;case"object":e==="config"&&t.push([e,await this.getConfigWithEnvPlaceholders()]),t.push([e,this[e]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(t);return s[E]=G.PLAN_GATES,s}static fromJson(t,s){const e=new P(s);for(const[r,o]of Object.entries(v))switch(o){case"map":e[r]=new Map(t[r]);break;case"object":if(r==="config"){e.setGlobalConfig(t[r]);break}e[r]=t[r];break;default:throw new Error("Invalid format")}e.config[m]=Z(e.config[m]||{});const a=t[E];return a&&_("PLAN_GATES",a),e}async getConfigWithEnvPlaceholders(){const t=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:e}=this.replacedEnvVars[s],a=s.split(":"),r=a.pop(),{error:o,value:l}=S(t,a);if(o||!T(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[r]=e}return t}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const t=Array.from(this.unsetEnvVars).filter(e=>!Y.includes(e));if(t.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${t.join(", ")}`;await w.panicOnBuildContentError(s)}}function Z(b){return M(b,t=>A.Ast.fromJSON(JSON.stringify(t)))}export{m as MARKDOC_PARTIALS_DATA_KEY,z as MARKDOC_PARTIALS_DEPS_KEY,P as Store,Ot as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
@@ -124,6 +124,9 @@ export type PathVersionInfo = {
124
124
  isDefault: boolean;
125
125
  versionFolderId: string;
126
126
  };
127
+ export type RedirectOptions = {
128
+ trackOriginalSource?: boolean;
129
+ };
127
130
  export type ProcessContentActions = {
128
131
  createSharedData(id: string, data: unknown, hash?: string): Promise<string>;
129
132
  addRouteSharedData(slug: string, dataKey: string, dataId: string): void;
@@ -142,7 +145,7 @@ export type ProcessContentActions = {
142
145
  addRoute: (route: PageRouteInit) => void;
143
146
  addApiRoute: (route: ApiRoute) => void;
144
147
  createRequestHandler: (id: string, importPath: string) => void;
145
- addRedirect: (from: string, to: RedirectConfig) => void;
148
+ addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
146
149
  createTemplate: (id: string, importPath: string) => string;
147
150
  addBrowserPlugin: (importPath: string) => void;
148
151
  registerServerPropsGetter: (id: string, importPath: string) => string;
@@ -176,7 +179,7 @@ export type AfterRoutesCreatedActions = {
176
179
  setSearchFacets: (facets: Map<string, SearchFacet>) => void;
177
180
  getSearchFacets: () => Map<string, SearchFacet>;
178
181
  setSearchEngine: (engine: SearchEngine) => void;
179
- addRedirect: (from: string, to: RedirectConfig) => void;
182
+ addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
180
183
  contentDir: string;
181
184
  outdir: string;
182
185
  serverOutDir: string;
@@ -3,4 +3,10 @@ export type WildcardRedirectsTree = {
3
3
  } & {
4
4
  '*'?: string;
5
5
  };
6
+ export type FollowRedirectChainResult = {
7
+ type: 'cycle';
8
+ cycle: string[];
9
+ } | {
10
+ type: 'no-cycle';
11
+ };
6
12
  //# sourceMappingURL=redirects.d.ts.map
@@ -1,39 +1,16 @@
1
- import type { RedirectsConfig, RedirectConfig } from '@redocly/config';
1
+ import type { RedirectConfig, RedirectsConfig } from '@redocly/config';
2
2
  import type { WildcardRedirectsTree } from '../../types';
3
3
  /**
4
- * Finds a matching redirect configuration for a given slug.
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
- * This function does NOT perform normalization internally.
8
7
  *
9
- * - First checks for a direct match in the `redirects` configuration.
10
- * - If no direct match is found, searches for wildcard redirects (patterns ending with `*`).
11
- * - For wildcard matches, preserves the remaining path after the wildcard prefix if the redirect target also ends with `*`.
8
+ * - For wildcard rules whose target ends with `*`, the remainder of the path is preserved.
9
+ * - `from` is the matched config key (path or pattern); exposed for `followRedirectChain` rule tracking.
12
10
  *
13
- * @param slug - The normalized (lowercased) URL slug to find a redirect for.
14
- * @param redirects - The redirects configuration object.
15
- * @param wildcardRedirectsTree - The wildcard redirects converted to a tree structure to optimize the search.
16
- * @returns An object containing the redirect `to` URL and optional `type`, or `null` if no redirect is found.
17
- *
18
- * @example
19
- * // Direct redirect
20
- * findRedirect('/old-page', { '/old-page': { to: '/new-page', type: 301 } }, {});
21
- * // Returns: { to: '/new-page', type: 301 }
22
- *
23
- * @example
24
- * // Wildcard redirect with path preservation
25
- * findRedirect('/docs/guide/intro', { '/docs/*': { to: '/documentation/*', type: 301 } }, { 'docs': { '*': '/docs/*' } });
26
- * // Returns: { to: '/documentation/guide/intro', type: 301 }
27
- *
28
- * @example
29
- * // Wildcard redirect to fixed destination
30
- * findRedirect('/old/anything', { '/old/*': { to: '/new-home', type: 302 } }, { 'old': { '*': '/old/*' } });
31
- * // Returns: { to: '/new-home', type: 302 }
32
- *
33
- * @example
34
- * // No match found
35
- * findRedirect('/some-page', {}, {});
36
- * // Returns: null
11
+ * @returns Redirect config with resolved `to`, plus `from`, or `null` if no rule matches or the rule would not change the slug.
37
12
  */
38
- export declare function findRedirect(slug: string, redirects: RedirectsConfig, wildcardRedirectsTree: WildcardRedirectsTree): WithRequired<RedirectConfig, 'to'> | null;
13
+ export declare function findRedirect(slug: string, redirects: RedirectsConfig, wildcardRedirectsTree: WildcardRedirectsTree): (WithRequired<RedirectConfig, 'to'> & {
14
+ from: string;
15
+ }) | null;
39
16
  //# sourceMappingURL=find-redirect.d.ts.map
@@ -1 +1 @@
1
- import{removeLeadingSlash as f}from"../../../utils/url/remove-leading-slash.js";function p(e,i,o){e=e.toLowerCase();const t=i[e];if(t&&t.to)return{to:t.to,type:t.type};const r=a(e,o);if(r){let n=i[r]?.to;if(n){if(n.endsWith("*")){const c=r.split("*")[0],d=e.substring(c.length);n=n.replace(/\*$/,d)}return{...i[r],to:n}}}return null}function a(e,i){const o=f(e).split("/");let t=i;for(const r of o){if(!t[r])break;t=t[r]}return t["*"]||null}export{p as findRedirect};
1
+ import{normalizeRouteSlug as a}from"../../../utils/path/normalize-route-slug.js";import{removeLeadingSlash as d}from"../../../utils/url/remove-leading-slash.js";function m(t,o,f){const r=o[t];if(r?.to){const i=r.to;return a(i).toLowerCase()===t?null:{to:i,type:r.type,from:t}}const e=u(t,f);if(!e)return null;const c=o[e],l=c?.to;if(!l)return null;let n=l;if(n.endsWith("*")){const i=e.split("*")[0];n=n.replace(/\*$/,t.substring(i.length))}return a(n).toLowerCase()===t?null:{...c,to:n,from:e}}function u(t,o){const f=d(t).split("/");let r=o;for(const e of f){if(!r[e])break;r=r[e]}return r["*"]||null}export{m as findRedirect};
@@ -0,0 +1,9 @@
1
+ import type { RedirectsConfig } from '@redocly/config';
2
+ import type { FollowRedirectChainResult, WildcardRedirectsTree } from '../../types';
3
+ /**
4
+ * Follows the redirect chain from startSlug. Returns when:
5
+ * - A cycle is detected (revisiting a slug or redirect rule) -> { type: 'cycle', cycle }
6
+ * - No redirect / external / wildcard -> { type: 'no-cycle' }
7
+ */
8
+ export declare function followRedirectChain(startSlug: string, initialChain: string[], redirects: RedirectsConfig, wildcardRedirectsTree: WildcardRedirectsTree, resolveWildcards?: boolean): FollowRedirectChainResult;
9
+ //# sourceMappingURL=follow-redirect-chain.d.ts.map
@@ -0,0 +1 @@
1
+ import{normalizeRouteSlug as a}from"../../../utils/path/normalize-route-slug.js";import{isLocalLink as p}from"../../../utils/path/is-local-link.js";import{findRedirect as u}from"./find-redirect.js";function x(r,n,c,y,d=!0){const f=new Set(n),i=new Set,e=[...n];function l(o){if(f.has(o)){const m=e.indexOf(o);return{type:"cycle",cycle:m>=0?e.slice(m).concat(o):[]}}f.add(o),e.push(o);const t=d?u(o,c,y):c[o]?.to?{to:c[o].to,from:o}:null;if(!t?.to||!p(t.to))return{type:"no-cycle"};if(t.from){if(i.has(t.from))return{type:"cycle",cycle:[...e,t.from,t.to,t.from]};i.add(t.from)}const s=a(t.to).toLowerCase();return s.endsWith("*")?{type:"no-cycle"}:l(s)}return l(r)}export{x as followRedirectChain};
@@ -0,0 +1,8 @@
1
+ import type { Store } from '../../store';
2
+ /**
3
+ * Validates redirects configuration: detects self-redirects, circular redirects,
4
+ * and redirects that point to a non-existent page. Reports each issue
5
+ * via the link checker so they appear in validation output.
6
+ */
7
+ export declare function validateRedirects(store: Store): void;
8
+ //# sourceMappingURL=validate-redirects.d.ts.map
@@ -0,0 +1 @@
1
+ import h from"node:path";import{existsSync as R,lstatSync as w,readdirSync as N}from"node:fs";import{CONFIG_FILE_NAME as y}from"../../../constants/common.js";import{PUBLIC_STATIC_FOLDER as F}from"../../constants/common.js";import{normalizeRouteSlug as s}from"../../../utils/path/normalize-route-slug.js";import{isLocalLink as C}from"../../../utils/path/is-local-link.js";import{reporter as T}from"../../tools/notifiers/reporter.js";import{followRedirectChain as k}from"./follow-redirect-chain.js";function D(e){const i=e.getConfig().redirects;if(!i)return;const o=e.getGlobalConfig("wildcardRedirectsTree")??{},c=e.getGlobalConfig("originalRedirectSources");for(const[r,l]of Object.entries(i)){const t=l?.to;if(!t||c&&!c.includes(r))continue;const d=r.toLowerCase(),L=t.toLowerCase(),m=r.endsWith("*"),f=t.endsWith("*");if(f)continue;const u=a=>{T.reportBrokenLink({type:"BROKEN_LINK",brokenLinkType:"LINK",sourceFileRelativePath:y,sourceFileLocation:{line:0},title:r,link:t,rawLink:t,message:a})};if(z(r,t,m,f)){u(`Redirect from "${r}" points to the same page. This causes an infinite redirect loop.`);continue}if(m&&!f&&C(t)){const a=s(r.split("*")[0]).toLowerCase(),p=s(L);if(a===p){u(`Circular redirect: "${r}" points to the same base path "${t}", causing an infinite redirect loop.`);continue}}if(!m&&!f&&C(t)){const a=s(d),p=s(L),g=k(p,[a],i,o,!1);if(g.type==="cycle"&&g.cycle.length>0){u(`Circular redirect: ${g.cycle.join(" \u2192 ")}.`);continue}}C(t)&&I(t,e)&&u(`Redirect target "${t}" does not exist. Check that the page or route exists.`)}}function z(e,n,i,o){if(!i&&!o){const c=s(e).toLowerCase(),r=s(n).toLowerCase();return c===r}return!1}function I(e,n){const i=s(e).toLowerCase();return n.slugHasRouteOrRedirect(i)?!1:!v(e,n)}function v(e,n){const o=decodeURI(e.split("?")[0].split("#")[0]).replace(/^\/+/,"");if(!o)return!1;const c=n.contentDir;return c?S(c,o)||S(h.join(c,F),o):!1}function S(e,n){const i=x(e,n);return i?P(i):!1}function x(e,n){const i=n.split("/").filter(Boolean);let o=e;for(const c of i){const r=h.join(o,c);if(R(r)){o=r;continue}let l;try{l=N(o)}catch{return null}const t=l.find(d=>d.toLowerCase()===c.toLowerCase());if(!t)return null;o=h.join(o,t)}return o}function P(e){if(!R(e))return!1;try{return w(e).isFile()}catch{return!1}}export{D as validateRedirects};
@@ -1,4 +1,4 @@
1
- import type { AuthProviderType, REDOCLY_TEAMS_RBAC, RbacScopeItems } from '@redocly/config';
1
+ import type { AuthProviderType, REDOCLY_TEAMS_RBAC, REDOCLY_ROUTE_RBAC, RbacScopeItems } from '@redocly/config';
2
2
  import type { UiAccessibleConfig } from '@redocly/config';
3
3
  export type GlobalData = UiAccessibleConfig & {
4
4
  authIdps?: Array<{
@@ -8,7 +8,12 @@ export type GlobalData = UiAccessibleConfig & {
8
8
  rbac?: object;
9
9
  apiProducts?: {
10
10
  [apiProductId: string]: {
11
- slug: string;
11
+ slug?: string;
12
+ [REDOCLY_TEAMS_RBAC]?: RbacScopeItems;
13
+ [REDOCLY_ROUTE_RBAC]?: {
14
+ slug?: string;
15
+ fsPath?: string;
16
+ };
12
17
  };
13
18
  };
14
19
  l10n?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/reef",
3
- "version": "0.132.0-next.4",
3
+ "version": "0.132.0-next.6",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,7 +29,7 @@
29
29
  "@opentelemetry/sdk-trace-web": "2.0.1",
30
30
  "@opentelemetry/semantic-conventions": "1.34.0",
31
31
  "@redocly/ajv": "8.18.0",
32
- "@redocly/openapi-core": "2.24.0",
32
+ "@redocly/openapi-core": "2.25.2",
33
33
  "@shikijs/transformers": "3.21.0",
34
34
  "@tanstack/react-query": "5.62.3",
35
35
  "@tanstack/react-table": "8.21.3",
@@ -67,7 +67,7 @@
67
67
  "openapi-sampler": "^1.7.2",
68
68
  "os-browserify": "0.3.0",
69
69
  "path-browserify": "1.0.1",
70
- "picomatch": "2.3.1",
70
+ "picomatch": "2.3.2",
71
71
  "react": "^19.2.4",
72
72
  "react-calendar": "5.1.0",
73
73
  "react-date-picker": "11.0.0",
@@ -91,14 +91,14 @@
91
91
  "xpath": "0.0.34",
92
92
  "yaml-ast-parser": "0.0.43",
93
93
  "zod": "^3.25.76",
94
- "@redocly/asyncapi-docs": "1.9.0-next.3",
95
- "@redocly/config": "0.44.2",
96
- "@redocly/graphql-docs": "1.9.0-next.3",
94
+ "@redocly/asyncapi-docs": "1.9.0-next.5",
95
+ "@redocly/graphql-docs": "1.9.0-next.5",
96
+ "@redocly/config": "0.46.0",
97
+ "@redocly/openapi-docs": "3.20.0-next.5",
97
98
  "@redocly/portal-legacy-ui": "0.15.0-next.0",
98
- "@redocly/portal-plugin-mock-server": "0.17.0-next.3",
99
+ "@redocly/portal-plugin-mock-server": "0.17.0-next.5",
99
100
  "@redocly/realm-asyncapi-sdk": "0.10.0-next.0",
100
- "@redocly/theme": "0.64.0-next.2",
101
- "@redocly/openapi-docs": "3.20.0-next.3"
101
+ "@redocly/theme": "0.64.0-next.3"
102
102
  },
103
103
  "peerDependencies": {
104
104
  "react": "^19.2.4",