@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.
- package/CHANGELOG.md +34 -0
- package/dist/client/app/CircularProgress.js +8 -8
- package/dist/server/config/env-schema.d.ts +3 -0
- package/dist/server/config/env-schemas/auth.d.ts +3 -0
- package/dist/server/config/env-schemas/auth.js +1 -1
- package/dist/server/plugins/catalog-entities/plugin.js +1 -1
- package/dist/server/plugins/config-parser/index.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/adapter-factory.d.ts +10 -3
- package/dist/server/plugins/dev-onboarding/api/adapter-factory.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/adapters/apigee/adapter.d.ts +5 -4
- package/dist/server/plugins/dev-onboarding/api/adapters/apigee/adapter.js +4 -4
- package/dist/server/plugins/dev-onboarding/api/adapters/gravitee/client.d.ts +3 -3
- package/dist/server/plugins/dev-onboarding/api/adapters/gravitee/client.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/middlewares/integrations.d.ts +3 -2
- package/dist/server/plugins/dev-onboarding/api/middlewares/integrations.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/routes/index.js +1 -1
- package/dist/server/plugins/dev-onboarding/api/types.d.ts +8 -0
- package/dist/server/plugins/dev-onboarding/template/components/ApiLogsActions.js +5 -5
- package/dist/server/plugins/dev-onboarding/template/components/DialogStyledComponents.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/MultiSelect.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/TextField.js +3 -3
- package/dist/server/plugins/enforce-login/index.js +1 -1
- package/dist/server/plugins/lifecycle.js +2 -2
- package/dist/server/plugins/markdown/search/to-markdown.js +24 -16
- package/dist/server/plugins/markdown/search/walk-sections.js +1 -1
- package/dist/server/plugins/openapi-docs/index.js +1 -1
- package/dist/server/plugins/openapi-docs/search-indexer.js +1 -1
- package/dist/server/store.d.ts +2 -2
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/common.d.ts +5 -2
- package/dist/server/types/redirects.d.ts +6 -0
- package/dist/server/utils/redirects/find-redirect.d.ts +8 -31
- package/dist/server/utils/redirects/find-redirect.js +1 -1
- package/dist/server/utils/redirects/follow-redirect-chain.d.ts +9 -0
- package/dist/server/utils/redirects/follow-redirect-chain.js +1 -0
- package/dist/server/utils/redirects/validate-redirects.d.ts +8 -0
- package/dist/server/utils/redirects/validate-redirects.js +1 -0
- package/dist/types/global-data.d.ts +7 -2
- 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
|
|
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:
|
|
5
|
-
height: ${({size:
|
|
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:
|
|
11
|
-
height: ${({size:
|
|
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{
|
|
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
|
|
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
|
|
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(
|
|
11
|
+
getAdapter(input: AdapterInput): Adapter;
|
|
5
12
|
}
|
|
6
13
|
//# sourceMappingURL=adapter-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ApigeeAdapter as
|
|
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:
|
|
17
|
+
catalogApiProducts: Map<string, CatalogApiProduct>;
|
|
18
18
|
allowApiProductsOutsideCatalog: boolean;
|
|
19
19
|
adapterId: string;
|
|
20
20
|
stage: string;
|
|
21
|
-
|
|
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
|
|
2
|
-
${await t.text()}`);const
|
|
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,
|
|
5
|
-
${await
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
`,
|
|
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
|
-
`,
|
|
149
|
+
`,_=l.div`
|
|
150
150
|
margin: var(--spacing-xs);
|
|
151
151
|
width: auto;
|
|
152
152
|
min-width: auto;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import i from"react";import c from"react-select";import{useTranslate as d}from"../../../../../client/app/hooks";const
|
|
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
|
|
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
|
-
`,
|
|
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
|
|
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
|
|
2
|
-
`+s.stack;
|
|
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
|
|
2
|
-
`);break;case"fence":const i=
|
|
3
|
-
`);break;case"item":
|
|
4
|
-
`).join(""));break;case"thead":const
|
|
5
|
-
`);break;case"tr":const
|
|
6
|
-
`);break;case"em":
|
|
7
|
-
`);break;case"hr":
|
|
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=
|
|
12
|
-
`);break;case"image":const
|
|
13
|
-
`);break;case"tag":if(
|
|
14
|
-
\`\`\`${`${
|
|
15
|
-
${
|
|
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(``);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
|
-
`;
|
|
18
|
-
\`\`\`${
|
|
19
|
-
${
|
|
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
|
-
`}
|
|
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
|
|
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
|
|
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
|
|
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};
|
package/dist/server/store.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Node } from '@markdoc/markdoc';
|
|
2
2
|
import type { JSX } from 'react';
|
|
3
3
|
import type { CommonError, GlobalData } from '../types/index.js';
|
|
4
|
-
import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree } 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;
|
package/dist/server/store.js
CHANGED
|
@@ -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;
|
|
@@ -1,39 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RedirectConfig, RedirectsConfig } from '@redocly/config';
|
|
2
2
|
import type { WildcardRedirectsTree } from '../../types';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Looks up a redirect rule for a slug
|
|
5
5
|
*
|
|
6
6
|
* **IMPORTANT:** The `slug` parameter must be normalized before passing to this function.
|
|
7
|
-
* This function does NOT perform normalization internally.
|
|
8
7
|
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
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
|
-
* @
|
|
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'>
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
95
|
-
"@redocly/
|
|
96
|
-
"@redocly/
|
|
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.
|
|
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.
|
|
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",
|