diff --git a/data/release-notes/enterprise-server/3-13/16.yml b/data/release-notes/enterprise-server/3-13/16.yml index 8ed9b3162f8a..5b7a1bcdd0cd 100644 --- a/data/release-notes/enterprise-server/3-13/16.yml +++ b/data/release-notes/enterprise-server/3-13/16.yml @@ -16,6 +16,8 @@ sections: Users of GitHub Actions could not view or manage Actions artifacts and logs if the global AWS STS endpoint was unavailable, because Actions did not use the configured regional STS endpoint. - | If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan. + - | + In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14] changes: - | Site administrators can now set rate limits for the WebSockets controller used for live updates, with `ghe-config app.github.web-sockets-rate-limit`. For more information, see [Controlling the rate for the live update service](/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits#controlling-the-rate-for-the-live-update-service). diff --git a/data/release-notes/enterprise-server/3-14/13.yml b/data/release-notes/enterprise-server/3-14/13.yml index 5e8bdd4258a9..f539584cc76d 100644 --- a/data/release-notes/enterprise-server/3-14/13.yml +++ b/data/release-notes/enterprise-server/3-14/13.yml @@ -20,6 +20,8 @@ sections: Organization owners had no audit log events to track organization announcements displayed on banners in the UI. - | If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan. + - | + In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14] changes: - | Site administrators can now set rate limits for the WebSockets controller used for live updates, with `ghe-config app.github.web-sockets-rate-limit`. For more information, see [Controlling the rate for the live update service](/admin/configuring-settings/configuring-user-applications-for-your-enterprise/configuring-rate-limits#controlling-the-rate-for-the-live-update-service). diff --git a/data/release-notes/enterprise-server/3-15/8.yml b/data/release-notes/enterprise-server/3-15/8.yml index 2a49880de83a..5ee472dca62a 100644 --- a/data/release-notes/enterprise-server/3-15/8.yml +++ b/data/release-notes/enterprise-server/3-15/8.yml @@ -22,6 +22,8 @@ sections: Organization owners had no audit log events to track organization announcements displayed on banners in the UI. - | If an Enterprise Managed User (EMU) pushed to their personal repository with both secret scanning and push protection enabled, the custom patterns defined at enterprise level were not being applied during the push protection scan. + - | + In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14] changes: - | To ensure critical integrations and automated systems have uninterrupted access, the `/repositories/:repository_id/collaborators` endpoints now honor the higher rate limits for exempt users set with `ghe-config app.github.rate-limiting-exempt-users ""`. diff --git a/data/release-notes/enterprise-server/3-16/4.yml b/data/release-notes/enterprise-server/3-16/4.yml index 2288fe4e663d..dd83fa24eea6 100644 --- a/data/release-notes/enterprise-server/3-16/4.yml +++ b/data/release-notes/enterprise-server/3-16/4.yml @@ -30,6 +30,8 @@ sections: When an administrator suspended a user from the site admin dashboard, the form required them to complete Digital Services Act (DSA) fields that are not relevant on GitHub Enterprise Server. - | After an appliance reboot, code scanning did not always trigger or process analyses. + - | + In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14] changes: - | Site administrators who test the Prometheus endpoint can now use 127.0.0.1 as a trusted IP address. Previously, only specific IPs were allowed for testing. diff --git a/data/release-notes/enterprise-server/3-17/1.yml b/data/release-notes/enterprise-server/3-17/1.yml index 7c95b1bb7a66..bf736f048349 100644 --- a/data/release-notes/enterprise-server/3-17/1.yml +++ b/data/release-notes/enterprise-server/3-17/1.yml @@ -30,6 +30,8 @@ sections: On instances with dangling commit graph lock files, recompute checksum operations were unexpectedly triggered. - | After an appliance reboot, code scanning did not always trigger or process analyses. + - | + In some situations, the kafka-lite service could cause client timeouts when processing consumer group membership sessions and expirations. [Updated: 2025-07-14] changes: - | Site administrators who test the Prometheus endpoint can now use 127.0.0.1 as a trusted IP address. Previously, only specific IPs were allowed for testing. diff --git a/data/ui.yml b/data/ui.yml index 0dab040b7da1..562571fa06c4 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -144,6 +144,7 @@ parameter_table: audit_logs: action: Action description: Description + reference: Reference graphql: reference: implements: {{ GraphQLItemTitle }} Implements diff --git a/src/audit-logs/components/GroupedEvents.tsx b/src/audit-logs/components/GroupedEvents.tsx index c8301971dc73..74c45936d0d0 100644 --- a/src/audit-logs/components/GroupedEvents.tsx +++ b/src/audit-logs/components/GroupedEvents.tsx @@ -22,16 +22,40 @@ export default function GroupedEvents({ auditLogEvents, category }: Props) { {t('action')} {t('description')} + {t('reference')} {auditLogEvents.map((event) => { + const renderReferenceLinks = () => { + if (!event.docs_reference_links || event.docs_reference_links === 'N/A') { + return null + } + + const links = event.docs_reference_links + .split(/[,\s]+/) + .map((link) => link.trim()) + .filter((link) => link && link !== 'N/A') + + const titles = event.docs_reference_titles + ? event.docs_reference_titles.split(', ') + : links + + return links.map((link, index) => ( + + {titles[index] || link} + {index < links.length - 1 && ', '} + + )) + } + return ( {event.action} {event.description} + {renderReferenceLinks()} ) })} diff --git a/src/audit-logs/lib/index.ts b/src/audit-logs/lib/index.ts index d5e7c590ef4c..c2af5a02d4dd 100644 --- a/src/audit-logs/lib/index.ts +++ b/src/audit-logs/lib/index.ts @@ -2,6 +2,7 @@ import path from 'path' import { readCompressedJsonFileFallback } from '@/frame/lib/read-json-file' import { getOpenApiVersion } from '@/versions/lib/all-versions' +import findPage from '@/frame/lib/find-page' import type { AuditLogEventT, CategorizedEvents, @@ -20,6 +21,61 @@ type PipelineConfig = { appendedDescriptions: Record } +type TitleResolutionContext = { + pages: Record + redirects: Record +} + +// Resolves docs_reference_links URLs to page titles +async function resolveReferenceLinksToTitles( + docsReferenceLinks: string, + context: TitleResolutionContext, +): Promise { + if (!docsReferenceLinks || docsReferenceLinks === 'N/A') { + return '' + } + + // Handle multiple comma-separated or space-separated links + const links = docsReferenceLinks + .split(/[,\s]+/) + .map((link) => link.trim()) + .filter((link) => link && link !== 'N/A') + + const titles = [] + for (const link of links) { + try { + const page = findPage(link, context.pages, context.redirects) + if (page) { + // Create a minimal context for rendering the title + const renderContext = { + currentLanguage: 'en', + currentVersion: 'free-pro-team@latest', + pages: context.pages, + redirects: context.redirects, + } + const title = await page.renderProp('title', renderContext, { textOnly: true }) + titles.push(title) + } else { + // If we can't resolve the link, use the original URL + titles.push(link) + } + } catch (error) { + // If resolution fails, use the original URL + console.warn( + `Failed to resolve title for link: ${link}`, + error instanceof Error + ? error instanceof Error + ? error.message + : String(error) + : String(error), + ) + titles.push(link) + } + } + + return titles.join(', ') +} + // get audit log event data for the requested page and version // // returns an array of event objects that look like this: @@ -87,17 +143,19 @@ export function getCategorizedAuditLogEvents(page: string, version: string) { } // Filters audit log events based on allowlist values. -// -// * eventsToCheck: events to consider -// * allowListvalues: allowlist values to filter by -// * currentEvents: events already collected -// * pipelineConfig: audit log pipeline config data -export function filterByAllowlistValues( - eventsToCheck: RawAuditLogEventT[], - allowListValues: string | string[], - currentEvents: AuditLogEventT[], - pipelineConfig: PipelineConfig, -) { +export async function filterByAllowlistValues({ + eventsToCheck, + allowListValues, + currentEvents = [], + pipelineConfig, + titleContext, +}: { + eventsToCheck: RawAuditLogEventT[] + allowListValues: string | string[] + currentEvents?: AuditLogEventT[] + pipelineConfig: PipelineConfig + titleContext?: TitleResolutionContext +}) { if (!Array.isArray(allowListValues)) allowListValues = [allowListValues] if (!currentEvents) currentEvents = [] @@ -112,12 +170,27 @@ export function filterByAllowlistValues( if (seen.has(event.action)) continue seen.add(event.action) - const minimal = { + const minimal: AuditLogEventT = { action: event.action, description: processAndGetEventDescription(event, eventAllowlists, pipelineConfig), docs_reference_links: event.docs_reference_links, } + // Resolve reference link titles if context is provided + if (titleContext && event.docs_reference_links && event.docs_reference_links !== 'N/A') { + try { + minimal.docs_reference_titles = await resolveReferenceLinksToTitles( + event.docs_reference_links, + titleContext, + ) + } catch (error) { + console.warn( + `Failed to resolve titles for event ${event.action}:`, + error instanceof Error ? error.message : String(error), + ) + } + } + minimalEvents.push(minimal) } } @@ -132,6 +205,7 @@ export function filterByAllowlistValues( // * currentEvents: events already collected // * pipelineConfig: audit log pipeline config data // * auditLogPage: the audit log page the event belongs to +// * titleContext: optional context for resolving reference link titles // // Mutates `currentGhesEvents` and updates it with any new filtered for audit // log events, the object maps GHES versions to page events for that version e.g.: @@ -148,13 +222,21 @@ export function filterByAllowlistValues( // user: [...], // }, // } -export function filterAndUpdateGhesDataByAllowlistValues( - eventsToCheck: RawAuditLogEventT[], - allowListValue: string, - currentGhesEvents: VersionedAuditLogData, - pipelineConfig: PipelineConfig, - auditLogPage: string, -) { +export async function filterAndUpdateGhesDataByAllowlistValues({ + eventsToCheck, + allowListValue, + currentGhesEvents, + pipelineConfig, + auditLogPage, + titleContext, +}: { + eventsToCheck: RawAuditLogEventT[] + allowListValue: string + currentGhesEvents: VersionedAuditLogData + pipelineConfig: PipelineConfig + auditLogPage: string + titleContext?: TitleResolutionContext +}) { if (!currentGhesEvents) currentGhesEvents = {} const seenByGhesVersion = new Map() @@ -173,12 +255,27 @@ export function filterAndUpdateGhesDataByAllowlistValues( if (seenByGhesVersion.get(fullGhesVersion)?.has(event.action)) continue if (ghesVersionAllowlists.includes(allowListValue)) { - const minimal = { + const minimal: AuditLogEventT = { action: event.action, description: processAndGetEventDescription(event, ghesVersionAllowlists, pipelineConfig), docs_reference_links: event.docs_reference_links, } + // Resolve reference link titles if context is provided + if (titleContext && event.docs_reference_links && event.docs_reference_links !== 'N/A') { + try { + minimal.docs_reference_titles = await resolveReferenceLinksToTitles( + event.docs_reference_links, + titleContext, + ) + } catch (error) { + console.warn( + `Failed to resolve titles for event ${event.action}:`, + error instanceof Error ? error.message : String(error), + ) + } + } + // we need to initialize as we go to build up the `minimalEvents` // object that we'll return which will contain the GHES events for // each versions + page type combos e.g. when processing GHES events diff --git a/src/audit-logs/scripts/sync.ts b/src/audit-logs/scripts/sync.ts index 4d3aa40a78ad..1b01b700e02c 100755 --- a/src/audit-logs/scripts/sync.ts +++ b/src/audit-logs/scripts/sync.ts @@ -15,6 +15,8 @@ import path from 'path' import { filterByAllowlistValues, filterAndUpdateGhesDataByAllowlistValues } from '../lib/index' import { getContents, getCommitSha } from '@/workflows/git-utils' import { latest, latestStable, releaseCandidate } from '@/versions/lib/enterprise-server-releases' +import { loadPages, loadPageMap } from '@/frame/lib/page-data' +import loadRedirects from '@/redirects/lib/precompile' import type { AuditLogEventT, VersionedAuditLogData } from '../types' if (!process.env.GITHUB_TOKEN) { @@ -53,6 +55,13 @@ async function main() { pipelineConfig.sha = mainSha await writeFile(configFilepath, JSON.stringify(pipelineConfig, null, 2)) + // Load pages and redirects for title resolution + console.log('Loading pages and redirects for title resolution...') + const pageList = await loadPages(undefined, ['en']) + const pages = await loadPageMap(pageList) + const redirects = await loadRedirects(pageList) + const titleContext = { pages, redirects } + // store an array of audit log event data keyed by version and audit log page, // will look like this (depends on supported GHES versions): // @@ -75,32 +84,39 @@ async function main() { // Wrapper around filterByAllowlistValues() because we always need all the // schema events and pipeline config data. const filter = (allowListValues: string | string[], currentEvents: AuditLogEventT[] = []) => - filterByAllowlistValues(schemaEvents, allowListValues, currentEvents, pipelineConfig) + filterByAllowlistValues({ + eventsToCheck: schemaEvents, + allowListValues, + currentEvents, + pipelineConfig, + titleContext, + }) // Wrapper around filterGhesByAllowlistValues() because we always need all the // schema events and pipeline config data. const filterAndUpdateGhes = ( - allowListValues: string, + allowListValue: string, auditLogPage: string, - currentEvents: VersionedAuditLogData, + currentGhesEvents: VersionedAuditLogData, ) => - filterAndUpdateGhesDataByAllowlistValues( - schemaEvents, - allowListValues, - currentEvents, + filterAndUpdateGhesDataByAllowlistValues({ + eventsToCheck: schemaEvents, + allowListValue, + currentGhesEvents, pipelineConfig, auditLogPage, - ) + titleContext, + }) auditLogData.fpt = {} - auditLogData.fpt.user = filter('user') - auditLogData.fpt.organization = filter(['organization', 'org_api_only']) + auditLogData.fpt.user = await filter('user') + auditLogData.fpt.organization = await filter(['organization', 'org_api_only']) auditLogData.ghec = {} - auditLogData.ghec.user = filter('user') - auditLogData.ghec.organization = filter('organization') - auditLogData.ghec.organization = filter('org_api_only', auditLogData.ghec.organization) - auditLogData.ghec.enterprise = filter('business') - auditLogData.ghec.enterprise = filter('business_api_only', auditLogData.ghec.enterprise) + auditLogData.ghec.user = await filter('user') + auditLogData.ghec.organization = await filter('organization') + auditLogData.ghec.organization = await filter('org_api_only', auditLogData.ghec.organization) + auditLogData.ghec.enterprise = await filter('business') + auditLogData.ghec.enterprise = await filter('business_api_only', auditLogData.ghec.enterprise) // GHES versions are numbered (i.e. "3.9", "3.10", etc.) and filterGhes() // gives us back an object of GHES versions to page events for each version @@ -114,11 +130,15 @@ async function main() { // so there's no single auditLogData.ghes like the other versions. const ghesVersionsAuditLogData = {} - filterAndUpdateGhes('business', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData) - filterAndUpdateGhes('business_api_only', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData) - filterAndUpdateGhes('user', AUDIT_LOG_PAGES.USER, ghesVersionsAuditLogData) - filterAndUpdateGhes('organization', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData) - filterAndUpdateGhes('org_api_only', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData) + await filterAndUpdateGhes('business', AUDIT_LOG_PAGES.ENTERPRISE, ghesVersionsAuditLogData) + await filterAndUpdateGhes( + 'business_api_only', + AUDIT_LOG_PAGES.ENTERPRISE, + ghesVersionsAuditLogData, + ) + await filterAndUpdateGhes('user', AUDIT_LOG_PAGES.USER, ghesVersionsAuditLogData) + await filterAndUpdateGhes('organization', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData) + await filterAndUpdateGhes('org_api_only', AUDIT_LOG_PAGES.ORGANIZATION, ghesVersionsAuditLogData) Object.assign(auditLogData, ghesVersionsAuditLogData) // We don't maintain the order of events as we process them so after filtering diff --git a/src/audit-logs/tests/unit/filter-events.ts b/src/audit-logs/tests/unit/filter-events.ts index 40d4bea2a678..18e02d387532 100644 --- a/src/audit-logs/tests/unit/filter-events.ts +++ b/src/audit-logs/tests/unit/filter-events.ts @@ -4,7 +4,7 @@ import { filterByAllowlistValues, filterAndUpdateGhesDataByAllowlistValues } fro import type { RawAuditLogEventT, VersionedAuditLogData } from '../../types' describe('audit log event fitering', () => { - test('matches single allowlist value', () => { + test('matches single allowlist value', async () => { const eventsToProcess: RawAuditLogEventT[] = [ { action: 'repo.create', @@ -15,14 +15,19 @@ describe('audit log event fitering', () => { }, ] - const filteredEvents = filterByAllowlistValues(eventsToProcess, 'user', [], { - sha: '', - appendedDescriptions: {}, + const filteredEvents = await filterByAllowlistValues({ + eventsToCheck: eventsToProcess, + allowListValues: 'user', + currentEvents: [], + pipelineConfig: { + sha: '', + appendedDescriptions: {}, + }, }) expect(filteredEvents[0].action).toEqual('repo.create') }) - test('matches multiple allowlist values', () => { + test('matches multiple allowlist values', async () => { const eventsToProcess: RawAuditLogEventT[] = [ { action: 'repo.create', @@ -40,15 +45,20 @@ describe('audit log event fitering', () => { }, ] - const filteredEvents = filterByAllowlistValues(eventsToProcess, ['user', 'organization'], [], { - sha: '', - appendedDescriptions: {}, + const filteredEvents = await filterByAllowlistValues({ + eventsToCheck: eventsToProcess, + allowListValues: ['user', 'organization'], + currentEvents: [], + pipelineConfig: { + sha: '', + appendedDescriptions: {}, + }, }) expect(filteredEvents[0].action).toEqual('repo.create') expect(filteredEvents.length).toEqual(2) }) - test('does not match non-matching allowlist value', () => { + test('does not match non-matching allowlist value', async () => { const eventsToProcess: RawAuditLogEventT[] = [ { action: 'repo.create', @@ -59,14 +69,19 @@ describe('audit log event fitering', () => { }, ] - const filteredEvents = filterByAllowlistValues(eventsToProcess, 'organization', [], { - sha: '', - appendedDescriptions: {}, + const filteredEvents = await filterByAllowlistValues({ + eventsToCheck: eventsToProcess, + allowListValues: 'organization', + currentEvents: [], + pipelineConfig: { + sha: '', + appendedDescriptions: {}, + }, }) expect(filteredEvents.length).toBe(0) }) - test('ghes filters and updates multiple ghes versions', () => { + test('ghes filters and updates multiple ghes versions', async () => { const eventsToProcess: RawAuditLogEventT[] = [ { action: 'repo.create', @@ -106,16 +121,16 @@ describe('audit log event fitering', () => { // this function mutates `currentEvents` so is updated as new GHES versioned // events from `eventToProcess` are processed and added. - filterAndUpdateGhesDataByAllowlistValues( - eventsToProcess, - 'user', - currentEvents, - { + await filterAndUpdateGhesDataByAllowlistValues({ + eventsToCheck: eventsToProcess, + allowListValue: 'user', + currentGhesEvents: currentEvents, + pipelineConfig: { sha: '', appendedDescriptions: {}, }, auditLogPage, - ) + }) const getActions = (version: string) => currentEvents[version][auditLogPage].map((event) => event.action) expect(getActions('ghes-3.10').includes('repo.create')).toBe(true) diff --git a/src/audit-logs/types.ts b/src/audit-logs/types.ts index 291c48f7e3c0..ccb03420ed1b 100644 --- a/src/audit-logs/types.ts +++ b/src/audit-logs/types.ts @@ -3,6 +3,8 @@ export type CategorizedEvents = Record export type AuditLogEventT = { action: string description: string + docs_reference_links?: string + docs_reference_titles?: string } export type RawAuditLogEventT = { @@ -10,6 +12,7 @@ export type RawAuditLogEventT = { action: string description: string docs_reference_links: string + docs_reference_titles?: string ghes: Record } diff --git a/src/content-render/types.ts b/src/content-render/types.ts new file mode 100644 index 000000000000..9fc352ce9ac5 --- /dev/null +++ b/src/content-render/types.ts @@ -0,0 +1,52 @@ +/** + * Types for the content-render module + */ + +/** + * Context interface for content rendering operations + */ +export interface Context { + currentLanguage?: string + autotitleLanguage?: string + currentVersion?: string + currentProduct?: string + markdownRequested?: boolean + pages?: any + redirects?: any + page?: { + fullPath: string + [key: string]: any + } + [key: string]: any +} + +/** + * Options for rendering operations + */ +export interface RenderOptions { + cache?: boolean | ((template: string, context: Context) => string | null) + filename?: string + textOnly?: boolean + [key: string]: any +} + +/** + * Unified processor plugin function type + */ +export type UnifiedPlugin = (context?: Context) => any + +/** + * VFile interface for unified processing + */ +export interface VFile { + toString(): string + [key: string]: any +} + +/** + * Unified processor interface + */ +export interface UnifiedProcessor { + process(content: string): Promise + use(plugin: any, ...args: any[]): UnifiedProcessor +} diff --git a/src/content-render/unified/module-types.d.ts b/src/content-render/unified/module-types.d.ts new file mode 100644 index 000000000000..a2dc5d4aa72d --- /dev/null +++ b/src/content-render/unified/module-types.d.ts @@ -0,0 +1,29 @@ +/** + * Type declarations for modules without TypeScript definitions + */ + +declare module 'remark-gemoji-to-emoji' { + import type { Plugin } from 'unified' + const plugin: Plugin + export default plugin +} + +declare module 'remark-remove-comments' { + import type { Plugin } from 'unified' + const plugin: Plugin + export default plugin +} + +declare module 'rehype-highlight' { + import type { Plugin } from 'unified' + import type { Options } from 'lowlight' + + interface HighlightOptions extends Options { + languages?: Record + subset?: boolean + aliases?: Record + } + + const plugin: Plugin<[HighlightOptions?]> + export default plugin +} diff --git a/src/content-render/unified/processor.js b/src/content-render/unified/processor.ts similarity index 76% rename from src/content-render/unified/processor.js rename to src/content-render/unified/processor.ts index 248e0767fdf9..5cce9c65bb1e 100644 --- a/src/content-render/unified/processor.js +++ b/src/content-render/unified/processor.ts @@ -29,8 +29,9 @@ import annotate from './annotate' import alerts from './alerts' import removeHtmlComments from 'remark-remove-comments' import remarkStringify from 'remark-stringify' +import type { Context, UnifiedProcessor } from '@/content-render/types' -export function createProcessor(context) { +export function createProcessor(context: Context): UnifiedProcessor { return ( unified() .use(remarkParse) @@ -44,11 +45,13 @@ export function createProcessor(context) { .use(remark2rehype, { allowDangerousHtml: true }) // HTML AST below vvv .use(slug) - .use(useEnglishHeadings, context) + // useEnglishHeadings plugin requires context with englishHeadings property + .use(useEnglishHeadings as any, context || {}) .use(headingLinks) .use(codeHeader) .use(annotate, context) - .use(highlight, { + // Using 'as any' for highlight plugin due to complex type mismatch between unified and rehype-highlight + .use(highlight as any, { languages: { ...common, graphql, dockerfile, http, groovy, erb, powershell }, subset: false, aliases: { @@ -71,18 +74,22 @@ export function createProcessor(context) { .use(rewriteForRowheaders) .use(rewriteImgSources) .use(rewriteAssetImgTags) - .use(alerts, context) + // alerts plugin requires context with alertTitles property + .use(alerts as any, context || {}) // HTML AST above ^^^ - .use(html) - // String below vvv + .use(html) as UnifiedProcessor // String below vvv ) } -export function createMarkdownOnlyProcessor(context) { - return unified().use(remarkParse).use(gfm).use(rewriteLocalLinks, context).use(remarkStringify) +export function createMarkdownOnlyProcessor(context: Context): UnifiedProcessor { + return unified() + .use(remarkParse) + .use(gfm) + .use(rewriteLocalLinks, context) + .use(remarkStringify) as UnifiedProcessor } -export function createMinimalProcessor(context) { +export function createMinimalProcessor(context: Context): UnifiedProcessor { return unified() .use(remarkParse) .use(gfm) @@ -90,5 +97,5 @@ export function createMinimalProcessor(context) { .use(remark2rehype, { allowDangerousHtml: true }) .use(slug) .use(raw) - .use(html) + .use(html) as UnifiedProcessor } diff --git a/src/fixtures/fixtures/data/ui.yml b/src/fixtures/fixtures/data/ui.yml index 0dab040b7da1..562571fa06c4 100644 --- a/src/fixtures/fixtures/data/ui.yml +++ b/src/fixtures/fixtures/data/ui.yml @@ -144,6 +144,7 @@ parameter_table: audit_logs: action: Action description: Description + reference: Reference graphql: reference: implements: {{ GraphQLItemTitle }} Implements