Eslint review, round 1 (#58168)
This commit is contained in:
@@ -67,63 +67,33 @@ export default [
|
|||||||
// JSX A11y recommended rules
|
// JSX A11y recommended rules
|
||||||
...jsxA11y.configs.recommended.rules,
|
...jsxA11y.configs.recommended.rules,
|
||||||
|
|
||||||
// TypeScript-specific overrides
|
// Overrides
|
||||||
'import/no-extraneous-dependencies': [
|
'import/no-extraneous-dependencies': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
packageDir: '.',
|
packageDir: '.',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'import/extensions': 'off',
|
'import/extensions': ['error', { json: 'always' }],
|
||||||
'no-console': 'off',
|
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||||
camelcase: 'off',
|
|
||||||
'no-shadow': 'off',
|
|
||||||
'prefer-template': 'off',
|
|
||||||
'no-constant-condition': 'off',
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'no-undef': 'off',
|
|
||||||
'no-use-before-define': 'off',
|
|
||||||
'no-redeclare': 'off', // Allow function overloads in TypeScript
|
|
||||||
'import/no-named-as-default-member': 'off',
|
|
||||||
'one-var': 'off',
|
|
||||||
'import/no-namespace': 'off',
|
|
||||||
'import/no-anonymous-default-export': 'off',
|
|
||||||
'object-shorthand': 'off',
|
|
||||||
'no-empty': 'off',
|
|
||||||
'prefer-const': 'off',
|
|
||||||
'import/no-named-as-default': 'off',
|
|
||||||
'no-useless-concat': 'off',
|
|
||||||
'func-style': 'off',
|
|
||||||
|
|
||||||
// TypeScript ESLint specific rules
|
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
|
'prefer-const': ['error', { destructuring: 'all' }],
|
||||||
|
|
||||||
// Disable GitHub plugin rules that were disabled in original config
|
// Rules that must be disabled
|
||||||
'github/array-foreach': 'off',
|
'no-redeclare': 'off', // Allow function overloads in TypeScript
|
||||||
'github/no-then': 'off',
|
'i18n-text/no-en': 'off', // This rule causes eslint to not run at all
|
||||||
|
'filenames/match-regex': 'off', // This rule causes eslint to not run at all
|
||||||
|
|
||||||
// Disable rules that might not exist or cause issues initially
|
// Disabled rules to review
|
||||||
'i18n-text/no-en': 'off',
|
'github/no-then': 'off', // 30+
|
||||||
'filenames/match-regex': 'off',
|
'@typescript-eslint/ban-ts-comment': 'off', // 50+
|
||||||
'eslint-comments/no-use': 'off',
|
'no-undef': 'off', // 50+
|
||||||
'eslint-comments/no-unused-disable': 'off',
|
'no-shadow': 'off', // 150+
|
||||||
'eslint-comments/no-unlimited-disable': 'off',
|
'prefer-template': 'off', // 150+
|
||||||
|
'github/array-foreach': 'off', // 250+
|
||||||
// Disable new ESLint 9 rules that are causing issues
|
camelcase: 'off', // 600+
|
||||||
'no-constant-binary-expression': 'off',
|
'no-console': 'off', // 800+
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off', // 1000+
|
||||||
// Disable stricter TypeScript rules initially
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-unused-expressions': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
|
||||||
'@typescript-eslint/no-wrapper-object-types': 'off',
|
|
||||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-function-type': 'off',
|
|
||||||
'@typescript-eslint/no-empty-object-type': 'off',
|
|
||||||
'@typescript-eslint/prefer-as-const': 'off',
|
|
||||||
|
|
||||||
// React/JSX specific rules
|
|
||||||
'jsx-a11y/no-onchange': 'off',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -53,7 +53,7 @@
|
|||||||
"index-general-search": "tsx src/search/scripts/index/index-cli general-search",
|
"index-general-search": "tsx src/search/scripts/index/index-cli general-search",
|
||||||
"index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh",
|
"index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh",
|
||||||
"labeler": "tsx .github/actions/labeler/labeler.ts",
|
"labeler": "tsx .github/actions/labeler/labeler.ts",
|
||||||
"lint": "eslint '**/*.{js,mjs,ts,tsx}'",
|
"lint": "eslint '**/*.{ts,tsx}'",
|
||||||
"lint-content": "tsx src/content-linter/scripts/lint-content.ts",
|
"lint-content": "tsx src/content-linter/scripts/lint-content.ts",
|
||||||
"lint-translation": "vitest src/content-linter/tests/lint-files.ts",
|
"lint-translation": "vitest src/content-linter/tests/lint-files.ts",
|
||||||
"liquid-markdown-tables": "tsx src/tools/scripts/liquid-markdown-tables/index.ts",
|
"liquid-markdown-tables": "tsx src/tools/scripts/liquid-markdown-tables/index.ts",
|
||||||
@@ -67,8 +67,8 @@
|
|||||||
"postinstall": "cp package-lock.json .installed.package-lock.json && echo \"Updated .installed.package-lock.json\" # see husky/post-checkout and husky/post-merge",
|
"postinstall": "cp package-lock.json .installed.package-lock.json && echo \"Updated .installed.package-lock.json\" # see husky/post-checkout and husky/post-merge",
|
||||||
"precompute-pageinfo": "tsx src/article-api/scripts/precompute-pageinfo.ts",
|
"precompute-pageinfo": "tsx src/article-api/scripts/precompute-pageinfo.ts",
|
||||||
"prepare": "husky src/workflows/husky",
|
"prepare": "husky src/workflows/husky",
|
||||||
"prettier": "prettier -w \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
|
"prettier": "prettier -w \"**/*.{ts,tsx,scss,yml,yaml}\"",
|
||||||
"prettier-check": "prettier -c \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
|
"prettier-check": "prettier -c \"**/*.{ts,tsx,scss,yml,yaml}\"",
|
||||||
"prevent-pushes-to-main": "tsx src/workflows/prevent-pushes-to-main.ts",
|
"prevent-pushes-to-main": "tsx src/workflows/prevent-pushes-to-main.ts",
|
||||||
"purge-fastly-edge-cache": "tsx src/workflows/purge-fastly-edge-cache.ts",
|
"purge-fastly-edge-cache": "tsx src/workflows/purge-fastly-edge-cache.ts",
|
||||||
"purge-fastly-edge-cache-per-language": "tsx src/languages/scripts/purge-fastly-edge-cache-per-language.ts",
|
"purge-fastly-edge-cache-per-language": "tsx src/languages/scripts/purge-fastly-edge-cache-per-language.ts",
|
||||||
@@ -105,12 +105,12 @@
|
|||||||
"what-docs-early-access-branch": "tsx src/early-access/scripts/what-docs-early-access-branch.ts"
|
"what-docs-early-access-branch": "tsx src/early-access/scripts/what-docs-early-access-branch.ts"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,mjs,ts,tsx}": "eslint --cache --fix",
|
"*.{ts,tsx}": "eslint --cache --fix",
|
||||||
"*.{js,mjs,scss,ts,tsx,yml,yaml}": "prettier --write",
|
"*.{scss,ts,tsx,yml,yaml}": "prettier --write",
|
||||||
"{content,data}/**/*.md": "npm run lint-content -- --precommit --paths"
|
"{content,data}/**/*.md": "npm run lint-content -- --precommit --paths"
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
"ext": "ts,js,json,yml,md,html,scss",
|
"ext": "ts,json,yml,md,html,scss",
|
||||||
"exec": "tsx",
|
"exec": "tsx",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"assets",
|
"assets",
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
"**/*.{ts,tsx,js,mjs}"
|
"**/*.{ts,tsx}"
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export default async function archivedEnterpriseVersions(
|
|||||||
// `x-host` is a custom header set by Fastly.
|
// `x-host` is a custom header set by Fastly.
|
||||||
// GLB automatically deletes the `x-forwarded-host` header.
|
// GLB automatically deletes the `x-forwarded-host` header.
|
||||||
const host = req.get('x-host') || req.get('x-forwarded-host') || req.get('host')
|
const host = req.get('x-host') || req.get('x-forwarded-host') || req.get('host')
|
||||||
let modifiedBody = body
|
const modifiedBody = body
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
`${OLD_AZURE_BLOB_ENTERPRISE_DIR}/${requestedVersion}/assets/cb-`,
|
`${OLD_AZURE_BLOB_ENTERPRISE_DIR}/${requestedVersion}/assets/cb-`,
|
||||||
`${ENTERPRISE_GH_PAGES_URL_PREFIX}${requestedVersion}/assets/cb-`,
|
`${ENTERPRISE_GH_PAGES_URL_PREFIX}${requestedVersion}/assets/cb-`,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as github from '@actions/github'
|
import github from '@actions/github'
|
||||||
import core from '@actions/core'
|
import core from '@actions/core'
|
||||||
|
|
||||||
const { GITHUB_TOKEN } = process.env
|
const { GITHUB_TOKEN } = process.env
|
||||||
|
|||||||
@@ -145,7 +145,9 @@ async function main(opts: MainOptions) {
|
|||||||
// Add exceptions
|
// Add exceptions
|
||||||
sourceFiles.push('.github/CONTRIBUTING.md')
|
sourceFiles.push('.github/CONTRIBUTING.md')
|
||||||
sourceFiles.push('README.md')
|
sourceFiles.push('README.md')
|
||||||
verbose && console.log(`${sourceFiles.length.toLocaleString()} source files found in total.`)
|
if (verbose) {
|
||||||
|
console.log(`${sourceFiles.length.toLocaleString()} source files found in total.`)
|
||||||
|
}
|
||||||
|
|
||||||
const allImages = new Set(
|
const allImages = new Set(
|
||||||
walk(
|
walk(
|
||||||
@@ -159,7 +161,9 @@ async function main(opts: MainOptions) {
|
|||||||
).filter((filePath) => !filePath.endsWith('.md')),
|
).filter((filePath) => !filePath.endsWith('.md')),
|
||||||
)
|
)
|
||||||
|
|
||||||
verbose && console.log(`${allImages.size.toLocaleString()} images found in total.`)
|
if (verbose) {
|
||||||
|
console.log(`${allImages.size.toLocaleString()} images found in total.`)
|
||||||
|
}
|
||||||
|
|
||||||
for (const sourceFile of sourceFiles) {
|
for (const sourceFile of sourceFiles) {
|
||||||
const content = fs.readFileSync(sourceFile, 'utf-8')
|
const content = fs.readFileSync(sourceFile, 'utf-8')
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ type MockResponse = {
|
|||||||
_json?: string
|
_json?: string
|
||||||
_send?: string
|
_send?: string
|
||||||
headers: Record<string, string>
|
headers: Record<string, string>
|
||||||
set?: (key: string | Object, value: string) => void
|
set?: (key: string | object, value: string) => void
|
||||||
removeHeader?: (key: string) => void
|
removeHeader?: (key: string) => void
|
||||||
hasHeader?: (key: string) => boolean
|
hasHeader?: (key: string) => boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ async function resolveReferenceLinksToTitles(
|
|||||||
// docs_reference_links: 'event reference links'
|
// docs_reference_links: 'event reference links'
|
||||||
// },
|
// },
|
||||||
// ]
|
// ]
|
||||||
export function getAuditLogEvents(page: string, version: string) {
|
export function getAuditLogEvents(page: string, version: string): AuditLogEventT[] {
|
||||||
const openApiVersion = getOpenApiVersion(version)
|
const openApiVersion = getOpenApiVersion(version)
|
||||||
const auditLogFileName = path.join(AUDIT_LOG_DATA_DIR, openApiVersion, `${page}.json`)
|
const auditLogFileName = path.join(AUDIT_LOG_DATA_DIR, openApiVersion, `${page}.json`)
|
||||||
|
|
||||||
@@ -115,14 +115,14 @@ export function getAuditLogEvents(page: string, version: string) {
|
|||||||
?.set(page, readCompressedJsonFileFallback(auditLogFileName))
|
?.set(page, readCompressedJsonFileFallback(auditLogFileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
const auditLogEvents = auditLogEventsCache.get(openApiVersion)?.get(page)!
|
const auditLogEvents = auditLogEventsCache.get(openApiVersion)?.get(page)
|
||||||
// If an event doesn't yet have a description (value will be empty string or
|
// If an event doesn't yet have a description (value will be empty string or
|
||||||
// "N/A"), then we don't show the event.
|
// "N/A"), then we don't show the event.
|
||||||
const filteredAuditLogEvents = auditLogEvents.filter(
|
const filteredAuditLogEvents = auditLogEvents?.filter(
|
||||||
(event) => event.description !== 'N/A' && event.description !== '',
|
(event) => event.description !== 'N/A' && event.description !== '',
|
||||||
)
|
)
|
||||||
|
|
||||||
return filteredAuditLogEvents
|
return filteredAuditLogEvents || []
|
||||||
}
|
}
|
||||||
|
|
||||||
// get categorized audit log event data for the requested page and version
|
// get categorized audit log event data for the requested page and version
|
||||||
@@ -137,7 +137,7 @@ export function getAuditLogEvents(page: string, version: string) {
|
|||||||
// repo: [ [Object] ],
|
// repo: [ [Object] ],
|
||||||
// user: [ [Object], [Object] ]
|
// user: [ [Object], [Object] ]
|
||||||
// }
|
// }
|
||||||
export function getCategorizedAuditLogEvents(page: string, version: string) {
|
export function getCategorizedAuditLogEvents(page: string, version: string): CategorizedEvents {
|
||||||
const events = getAuditLogEvents(page, version)
|
const events = getAuditLogEvents(page, version)
|
||||||
const openApiVersion = getOpenApiVersion(version)
|
const openApiVersion = getOpenApiVersion(version)
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ export function getCategorizedAuditLogEvents(page: string, version: string) {
|
|||||||
categorizedAuditLogEventsCache.get(openApiVersion)?.set(page, categorizeEvents(events))
|
categorizedAuditLogEventsCache.get(openApiVersion)?.set(page, categorizeEvents(events))
|
||||||
}
|
}
|
||||||
|
|
||||||
return categorizedAuditLogEventsCache.get(openApiVersion)?.get(page)!
|
return categorizedAuditLogEventsCache.get(openApiVersion)?.get(page) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filters audit log events based on allowlist values.
|
// Filters audit log events based on allowlist values.
|
||||||
|
|||||||
@@ -86,7 +86,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
|||||||
Object.keys(auditLogEvents).map((category) => category),
|
Object.keys(auditLogEvents).map((category) => category),
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
auditLogEventsMiniTocs && miniTocItems.push(...auditLogEventsMiniTocs)
|
if (auditLogEventsMiniTocs) {
|
||||||
|
miniTocItems.push(...auditLogEventsMiniTocs)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as nodePath from 'path'
|
import nodePath from 'path'
|
||||||
import { reportingConfig } from '@/content-linter/style/github-docs'
|
import { reportingConfig } from '@/content-linter/style/github-docs'
|
||||||
|
|
||||||
interface LintFlaw {
|
interface LintFlaw {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const liquidIfversionVersions = {
|
|||||||
// The versions frontmatter object or all versions if the file
|
// The versions frontmatter object or all versions if the file
|
||||||
// being processed is a data file.
|
// being processed is a data file.
|
||||||
const fm = getFrontmatter(params.lines)
|
const fm = getFrontmatter(params.lines)
|
||||||
let content = fm ? getFrontmatterLines(params.lines).join('\n') : params.lines.join('\n')
|
const content = fm ? getFrontmatterLines(params.lines).join('\n') : params.lines.join('\n')
|
||||||
|
|
||||||
const fileVersionsFm = params.name.startsWith('data')
|
const fileVersionsFm = params.name.startsWith('data')
|
||||||
? { ghec: '*', ghes: '*', fpt: '*' }
|
? { ghec: '*', ghes: '*', fpt: '*' }
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const noteWarningFormatting = {
|
|||||||
|
|
||||||
// Collect content inside legacy notes
|
// Collect content inside legacy notes
|
||||||
if (inLegacyNote) {
|
if (inLegacyNote) {
|
||||||
noteContent.push({ text: line, lineNumber: lineNumber })
|
noteContent.push({ text: line, lineNumber })
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ function findOutdatedTerminologyMatches(line: string): MatchInfo[] {
|
|||||||
start: match.index,
|
start: match.index,
|
||||||
end: match.index + match[0].length,
|
end: match.index + match[0].length,
|
||||||
text: match[0],
|
text: match[0],
|
||||||
replacement: replacement,
|
replacement,
|
||||||
outdatedTerm: outdatedTerm,
|
outdatedTerm,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,7 +240,9 @@ if (ymlToLint.length === 0) {
|
|||||||
describe('lint yaml content', () => {
|
describe('lint yaml content', () => {
|
||||||
if (ymlToLint.length < 1) return
|
if (ymlToLint.length < 1) return
|
||||||
describe.each(ymlToLint)('%s', (yamlRelPath, yamlAbsPath) => {
|
describe.each(ymlToLint)('%s', (yamlRelPath, yamlAbsPath) => {
|
||||||
let dictionary, isEarlyAccess, fileContents
|
let dictionary
|
||||||
|
let isEarlyAccess
|
||||||
|
let fileContents
|
||||||
// This variable is used to determine if the file was parsed successfully.
|
// This variable is used to determine if the file was parsed successfully.
|
||||||
// When `yaml.load()` fails to parse the file, it is overwritten with the error message.
|
// When `yaml.load()` fails to parse the file, it is overwritten with the error message.
|
||||||
// `false` is intentionally chosen since `null` and `undefined` are valid return values.
|
// `false` is intentionally chosen since `null` and `undefined` are valid return values.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ const notRegex = /(?:^|\s)not\s/
|
|||||||
// native Liquid `if` block tag. It has special handling for statements like {% ifversion ghes < 3.0 %},
|
// native Liquid `if` block tag. It has special handling for statements like {% ifversion ghes < 3.0 %},
|
||||||
// using semver to evaluate release numbers instead of doing standard number comparisons, which
|
// using semver to evaluate release numbers instead of doing standard number comparisons, which
|
||||||
// don't work the way we want because they evaluate 3.2 > 3.10 = true.
|
// don't work the way we want because they evaluate 3.2 > 3.10 = true.
|
||||||
export default class extends Tag {
|
export default class Ifversion extends Tag {
|
||||||
tagToken: TagToken
|
tagToken: TagToken
|
||||||
branches: Branch[]
|
branches: Branch[]
|
||||||
elseTemplates: Template[]
|
elseTemplates: Template[]
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const Prompt: LiquidTag = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Render the inner Markdown, wrap in <code>, then append the SVG
|
// Render the inner Markdown, wrap in <code>, then append the SVG
|
||||||
render: function* (scope: any): Generator<any, string, unknown> {
|
*render(scope: any): Generator<any, string, unknown> {
|
||||||
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
|
const content = yield this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||||
|
|
||||||
// build a URL with the prompt text encoded as query parameter
|
// build a URL with the prompt text encoded as query parameter
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export const Spotlight = {
|
|||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function* (scope: Record<string, unknown>): Generator<unknown, unknown, unknown> {
|
*render(scope: Record<string, unknown>): Generator<unknown, unknown, unknown> {
|
||||||
const output = yield this.liquid!.renderer.renderTemplates(this.templates, scope)
|
const output = yield this.liquid!.renderer.renderTemplates(this.templates, scope)
|
||||||
|
|
||||||
return yield this.liquid!.parseAndRender(template, {
|
return yield this.liquid!.parseAndRender(template, {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const Tool = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// scope is a Liquid scope object, Generator yields/returns Liquid template values - no TypeScript definitions available
|
// scope is a Liquid scope object, Generator yields/returns Liquid template values - no TypeScript definitions available
|
||||||
render: function* (scope: any): Generator<any, any, any> {
|
*render(scope: any): Generator<any, any, any> {
|
||||||
const output = yield this.liquid.renderer.renderTemplates(this.templates, scope)
|
const output = yield this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||||
return yield this.liquid.parseAndRender(template, {
|
return yield this.liquid.parseAndRender(template, {
|
||||||
tagName: this.tagName,
|
tagName: this.tagName,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as github from '@actions/github'
|
import github from '@actions/github'
|
||||||
import core from '@actions/core'
|
import core from '@actions/core'
|
||||||
import { program } from 'commander'
|
import { program } from 'commander'
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe('data-directory', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('option: preprocess function', async () => {
|
test('option: preprocess function', async () => {
|
||||||
const preprocess = function (content: string) {
|
function preprocess(content: string) {
|
||||||
return content.replace('markdown', 'MARKDOWN')
|
return content.replace('markdown', 'MARKDOWN')
|
||||||
}
|
}
|
||||||
const data = dataDirectory(fixturesDir, { preprocess })
|
const data = dataDirectory(fixturesDir, { preprocess })
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ const sentValidationErrors = new QuickLRU({
|
|||||||
// to prevent sending multiple validation errors that can spam requests to Hydro
|
// to prevent sending multiple validation errors that can spam requests to Hydro
|
||||||
const getValidationErrorHash = (validateErrors: ErrorObject[]) => {
|
const getValidationErrorHash = (validateErrors: ErrorObject[]) => {
|
||||||
// limit to 10 second windows
|
// limit to 10 second windows
|
||||||
const window: Number = Math.floor(new Date().getTime() / 10000)
|
const window: number = Math.floor(new Date().getTime() / 10000)
|
||||||
return `${window}:${(validateErrors || [])
|
return `${window}:${(validateErrors || [])
|
||||||
.map((error: ErrorObject) => error.message + error.instancePath + JSON.stringify(error.params))
|
.map((error: ErrorObject) => error.message + error.instancePath + JSON.stringify(error.params))
|
||||||
.join(':')}`
|
.join(':')}`
|
||||||
|
|||||||
@@ -105,12 +105,12 @@ export type EventPropsByType = {
|
|||||||
link_samepage?: boolean
|
link_samepage?: boolean
|
||||||
link_container?: string
|
link_container?: string
|
||||||
}
|
}
|
||||||
[EventType.page]: {}
|
[EventType.page]: { type: string } // no unique properties
|
||||||
[EventType.preference]: {
|
[EventType.preference]: {
|
||||||
preference_name: string
|
preference_name: string
|
||||||
preference_value: string
|
preference_value: string
|
||||||
}
|
}
|
||||||
[EventType.print]: {}
|
[EventType.print]: { type: string } // no unique properties
|
||||||
[EventType.search]: {
|
[EventType.search]: {
|
||||||
search_query: string
|
search_query: string
|
||||||
search_context?: string
|
search_context?: string
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ describe('annotations', () => {
|
|||||||
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
|
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
|
||||||
expect(noteTexts).toEqual([
|
expect(noteTexts).toEqual([
|
||||||
'Configures this workflow to run every time a change is pushed to the branch called release.',
|
'Configures this workflow to run every time a change is pushed to the branch called release.',
|
||||||
'This job checks out the repository contents ...\n' + "And here's the second comment line.",
|
"This job checks out the repository contents ...\nAnd here's the second comment line.",
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -748,7 +748,7 @@ test.describe('survey', () => {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
Object.defineProperty(document, 'visibilityState', {
|
Object.defineProperty(document, 'visibilityState', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function () {
|
get() {
|
||||||
return 'hidden'
|
return 'hidden'
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -803,7 +803,7 @@ test.describe('survey', () => {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
Object.defineProperty(document, 'visibilityState', {
|
Object.defineProperty(document, 'visibilityState', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: function () {
|
get() {
|
||||||
return 'hidden'
|
return 'hidden'
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export const SharedUIContextProvider = ({ children }: { children: React.ReactNod
|
|||||||
return (
|
return (
|
||||||
<SharedUIContext.Provider
|
<SharedUIContext.Provider
|
||||||
value={{
|
value={{
|
||||||
hasOpenHeaderNotifications: hasOpenHeaderNotifications,
|
hasOpenHeaderNotifications,
|
||||||
setHasOpenHeaderNotifications: setHasOpenHeaderNotifications,
|
setHasOpenHeaderNotifications,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMainContext } from '@/frame/components/context/MainContext'
|
import { useMainContext } from '@/frame/components/context/MainContext'
|
||||||
|
|
||||||
export type FeatureFlags = {}
|
export type FeatureFlags = Record<string, string>
|
||||||
|
|
||||||
export const useFeatureFlags = (): FeatureFlags => {
|
export const useFeatureFlags = (): FeatureFlags => {
|
||||||
const { featureFlags } = useMainContext()
|
const { featureFlags } = useMainContext()
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ export const UnrenderedMarkdownContent = ({
|
|||||||
sendEvent({
|
sendEvent({
|
||||||
type: EventType.clipboard,
|
type: EventType.clipboard,
|
||||||
clipboard_operation: 'copy',
|
clipboard_operation: 'copy',
|
||||||
eventGroupKey: eventGroupKey,
|
eventGroupKey,
|
||||||
eventGroupId: eventGroupId,
|
eventGroupId,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
></IconButton>
|
></IconButton>
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ function readFrontmatter(markdown: string, opts: ReadFrontmatterOptions = {}) {
|
|||||||
const schema = opts.schema || { type: 'object', properties: {} }
|
const schema = opts.schema || { type: 'object', properties: {} }
|
||||||
const filepath = opts.filepath || null
|
const filepath = opts.filepath || null
|
||||||
|
|
||||||
let content, data
|
let content
|
||||||
|
let data
|
||||||
|
|
||||||
try {
|
try {
|
||||||
;({ content, data } = matter(markdown))
|
;({ content, data } = matter(markdown))
|
||||||
|
|||||||
@@ -5,9 +5,18 @@ import { createLogger } from '@/observability/logger'
|
|||||||
|
|
||||||
const logger = createLogger(import.meta.url)
|
const logger = createLogger(import.meta.url)
|
||||||
|
|
||||||
|
type WarmServerResult = {
|
||||||
|
pages: Awaited<ReturnType<typeof loadPageMap>>
|
||||||
|
redirects: Awaited<ReturnType<typeof loadRedirects>>
|
||||||
|
unversionedTree: Awaited<ReturnType<typeof loadUnversionedTree>>
|
||||||
|
siteTree: Awaited<ReturnType<typeof loadSiteTree>>
|
||||||
|
pageList: Awaited<ReturnType<typeof loadPages>>
|
||||||
|
pageMap: Awaited<ReturnType<typeof loadPageMap>>
|
||||||
|
}
|
||||||
|
|
||||||
// Instrument these functions so that
|
// Instrument these functions so that
|
||||||
// it's wrapped in a timer that reports to Datadog
|
// it's wrapped in a timer that reports to Datadog
|
||||||
const dog: Record<string, Function> = {
|
const dog = {
|
||||||
loadUnversionedTree: statsd.asyncTimer(loadUnversionedTree, 'load_unversioned_tree'),
|
loadUnversionedTree: statsd.asyncTimer(loadUnversionedTree, 'load_unversioned_tree'),
|
||||||
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree'),
|
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree'),
|
||||||
loadPages: statsd.asyncTimer(loadPages, 'load_pages'),
|
loadPages: statsd.asyncTimer(loadPages, 'load_pages'),
|
||||||
@@ -17,9 +26,9 @@ const dog: Record<string, Function> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For multiple-triggered Promise sharing
|
// For multiple-triggered Promise sharing
|
||||||
let promisedWarmServer: any
|
let promisedWarmServer: Promise<WarmServerResult> | undefined
|
||||||
|
|
||||||
async function warmServer(languagesOnly = []) {
|
async function warmServer(languagesOnly: string[] = []): Promise<WarmServerResult> {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
@@ -42,6 +51,7 @@ async function warmServer(languagesOnly = []) {
|
|||||||
unversionedTree,
|
unversionedTree,
|
||||||
siteTree,
|
siteTree,
|
||||||
pageList,
|
pageList,
|
||||||
|
pageMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ if (process.env.ELASTICSEARCH_URL) {
|
|||||||
createProxyMiddleware({
|
createProxyMiddleware({
|
||||||
target: 'https://docs.github.com',
|
target: 'https://docs.github.com',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: function (path, req: ExtendedRequest) {
|
pathRewrite(path, req: ExtendedRequest) {
|
||||||
return req.originalUrl
|
return req.originalUrl
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function blockIndex(path: string) {
|
|||||||
return pathRegExps.some((pathRe) => pathRe.test(path))
|
return pathRegExps.some((pathRe) => pathRe.test(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
const middleware = function blockRobots(req: Request, res: Response, next: NextFunction) {
|
function middleware(req: Request, res: Response, next: NextFunction) {
|
||||||
if (blockIndex(req.path)) res.set('x-robots-tag', 'noindex')
|
if (blockIndex(req.path)) res.set('x-robots-tag', 'noindex')
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ async function getTocItems(node: Tree, context: Context, opts: Options): Promise
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let childTocItems = []
|
const childTocItems = []
|
||||||
if (child.childPages) {
|
if (child.childPages) {
|
||||||
childTocItems.push(...(await getTocItems(child, context, opts)))
|
childTocItems.push(...(await getTocItems(child, context, opts)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ const DEFAULT_OPTIONS = {
|
|||||||
crossOriginResourcePolicy: true,
|
crossOriginResourcePolicy: true,
|
||||||
crossOriginEmbedderPolicy: false, // doesn't work with youtube
|
crossOriginEmbedderPolicy: false, // doesn't work with youtube
|
||||||
referrerPolicy: {
|
referrerPolicy: {
|
||||||
// See docs-engineering #2426
|
policy: 'no-referrer-when-downgrade' as const,
|
||||||
// The `... as 'no-referrer-when-downgrade'` is a workaround for TypeScript
|
|
||||||
policy: 'no-referrer-when-downgrade' as 'no-referrer-when-downgrade',
|
|
||||||
},
|
},
|
||||||
// This module defines a Content Security Policy (CSP) to disallow
|
// This module defines a Content Security Policy (CSP) to disallow
|
||||||
// inline scripts and content from untrusted sources.
|
// inline scripts and content from untrusted sources.
|
||||||
|
|||||||
@@ -78,11 +78,15 @@ const ENABLE_FASTLY_TESTING = JSON.parse(process.env.ENABLE_FASTLY_TESTING || 'f
|
|||||||
|
|
||||||
// Catch unhandled promise rejections and passing them to Express's error handler
|
// Catch unhandled promise rejections and passing them to Express's error handler
|
||||||
// https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
// https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
||||||
const asyncMiddleware = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
|
const asyncMiddleware =
|
||||||
Promise.resolve(fn(req, res, next)).catch(next)
|
<TReq extends Request = Request, T = void>(
|
||||||
}
|
fn: (req: TReq, res: Response, next: NextFunction) => T | Promise<T>,
|
||||||
|
) =>
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
Promise.resolve(fn(req as TReq, res, next)).catch(next)
|
||||||
|
}
|
||||||
|
|
||||||
export default function (app: Express) {
|
export default function index(app: Express) {
|
||||||
// *** Request connection management ***
|
// *** Request connection management ***
|
||||||
if (!isTest) app.use(timeout(MAX_REQUEST_TIMEOUT))
|
if (!isTest) app.use(timeout(MAX_REQUEST_TIMEOUT))
|
||||||
app.use(abort)
|
app.use(abort)
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ async function isExistingIssue(
|
|||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const { labels, searchQuery, titleMatch } = opts
|
const { labels, searchQuery, titleMatch } = opts
|
||||||
const labelQuery = labels && labels.map((label) => `label:"${encodeURI(label)}"`).join('+')
|
const labelQuery = labels && labels.map((label) => `label:"${encodeURI(label)}"`).join('+')
|
||||||
let query = encodeURIComponent('is:issue ' + `repo:${repo} `)
|
let query = encodeURIComponent(`is:issue repo:${repo} `)
|
||||||
|
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
query += '+' + searchQuery
|
query += '+' + searchQuery
|
||||||
|
|||||||
@@ -63,10 +63,11 @@ main()
|
|||||||
async function main() {
|
async function main() {
|
||||||
console.log(`Archiving Enterprise version: ${version}`)
|
console.log(`Archiving Enterprise version: ${version}`)
|
||||||
|
|
||||||
let pageList: PageList, urls: Array<string>
|
let pageList: PageList
|
||||||
|
let urls: Array<string>
|
||||||
if (singlePage) {
|
if (singlePage) {
|
||||||
const pageName = singlePage.trim().startsWith('/') ? singlePage.slice(1) : singlePage
|
const pageName = singlePage.trim().startsWith('/') ? singlePage.slice(1) : singlePage
|
||||||
const urls = languageKeys
|
urls = languageKeys
|
||||||
.map((key) => `/${key}/enterprise-server@${version}/${pageName}`)
|
.map((key) => `/${key}/enterprise-server@${version}/${pageName}`)
|
||||||
.map((href) => `${host}${href}`)
|
.map((href) => `${host}${href}`)
|
||||||
console.log(`\nScraping HTML for a single page only:\n${urls.join('\n')}\n`)
|
console.log(`\nScraping HTML for a single page only:\n${urls.join('\n')}\n`)
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
|
interface ScraperResource {
|
||||||
|
isHtml(): boolean
|
||||||
|
isCss(): boolean
|
||||||
|
getText(): string
|
||||||
|
getFilename(): string
|
||||||
|
encoding: BufferEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResourceSavedArgs {
|
||||||
|
resource: ScraperResource
|
||||||
|
}
|
||||||
|
|
||||||
export class RewriteAssetPathsPlugin {
|
export class RewriteAssetPathsPlugin {
|
||||||
tempDirectory: string
|
tempDirectory: string
|
||||||
localDev: boolean
|
localDev: boolean
|
||||||
@@ -12,8 +24,10 @@ export class RewriteAssetPathsPlugin {
|
|||||||
this.replaceUrl = replaceUrl
|
this.replaceUrl = replaceUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(registerAction: Function) {
|
apply(
|
||||||
registerAction('onResourceSaved', async ({ resource }: any) => {
|
registerAction: (event: string, callback: (args: ResourceSavedArgs) => Promise<void>) => void,
|
||||||
|
) {
|
||||||
|
registerAction('onResourceSaved', async ({ resource }: ResourceSavedArgs) => {
|
||||||
// Show some activity
|
// Show some activity
|
||||||
process.stdout.write('.')
|
process.stdout.write('.')
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,9 @@ export async function getAppsServerSideProps(
|
|||||||
? Object.values(appsItems).map((item: any) => item.displayTitle!)
|
? Object.values(appsItems).map((item: any) => item.displayTitle!)
|
||||||
: Object.keys(appsItems)
|
: Object.keys(appsItems)
|
||||||
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
|
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
|
||||||
appMiniToc && miniTocItems.push(...appMiniToc)
|
if (appMiniToc) {
|
||||||
|
miniTocItems.push(...appMiniToc)
|
||||||
|
}
|
||||||
|
|
||||||
return { currentVersion, appsItems, categoriesWithoutSubcategories }
|
return { currentVersion, appsItems, categoriesWithoutSubcategories }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ function getDisplayTitle(
|
|||||||
: `"${sentenceCase(title)}" ` + resourceGroup + ' permissions'
|
: `"${sentenceCase(title)}" ` + resourceGroup + ' permissions'
|
||||||
: !resourceGroup
|
: !resourceGroup
|
||||||
? sentenceCase(title) + ' permissions'
|
? sentenceCase(title) + ' permissions'
|
||||||
: sentenceCase(resourceGroup) + ' permissions for ' + `"${title}"`
|
: sentenceCase(resourceGroup) + ` permissions for "${title}"`
|
||||||
|
|
||||||
return { title, displayTitle }
|
return { title, displayTitle }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
type Props = {
|
type Props = {
|
||||||
mainContext: MainContextT
|
mainContext: MainContextT
|
||||||
automatedPageContext: AutomatedPageContextT
|
automatedPageContext: AutomatedPageContextT
|
||||||
schema: Object
|
schema: object
|
||||||
language: string
|
language: string
|
||||||
graphqlPageName: string
|
graphqlPageName: string
|
||||||
objects?: ObjectT[]
|
objects?: ObjectT[]
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const versionsToBuild = Object.keys(allVersions)
|
|||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
let allIgnoredChanges = []
|
const allIgnoredChanges = []
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
for (const version of versionsToBuild) {
|
for (const version of versionsToBuild) {
|
||||||
|
|||||||
@@ -2,12 +2,38 @@ import type { ProductGroupT } from '@/landings/components/ProductSelections'
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Link } from '@/frame/components/Link'
|
import { Link } from '@/frame/components/Link'
|
||||||
import * as Octicons from '@primer/octicons-react'
|
import {
|
||||||
import { LinkExternalIcon } from '@primer/octicons-react'
|
LinkExternalIcon,
|
||||||
|
RocketIcon,
|
||||||
|
CommentDiscussionIcon,
|
||||||
|
CopilotIcon,
|
||||||
|
GearIcon,
|
||||||
|
ShieldLockIcon,
|
||||||
|
DeviceMobileIcon,
|
||||||
|
ProjectIcon,
|
||||||
|
OrganizationIcon,
|
||||||
|
CodeSquareIcon,
|
||||||
|
GlobeIcon,
|
||||||
|
PencilIcon,
|
||||||
|
} from '@primer/octicons-react'
|
||||||
|
|
||||||
type ProductSelectionCardProps = {
|
type ProductSelectionCardProps = {
|
||||||
group: ProductGroupT
|
group: ProductGroupT
|
||||||
}
|
}
|
||||||
|
const octiconMap: { [name: string]: React.FunctionComponent } = {
|
||||||
|
LinkExternalIcon,
|
||||||
|
RocketIcon,
|
||||||
|
CommentDiscussionIcon,
|
||||||
|
CopilotIcon,
|
||||||
|
GearIcon,
|
||||||
|
ShieldLockIcon,
|
||||||
|
DeviceMobileIcon,
|
||||||
|
ProjectIcon,
|
||||||
|
OrganizationIcon,
|
||||||
|
CodeSquareIcon,
|
||||||
|
GlobeIcon,
|
||||||
|
PencilIcon,
|
||||||
|
}
|
||||||
|
|
||||||
export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
|
export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
|
||||||
// Don't display the group if it has no children due to versioning
|
// Don't display the group if it has no children due to versioning
|
||||||
@@ -27,9 +53,11 @@ export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else if (group.octicon) {
|
} else if (group.octicon) {
|
||||||
const octicon: React.FunctionComponent = (
|
const octicon: React.FunctionComponent = octiconMap[group.octicon]
|
||||||
Octicons as { [name: string]: React.FunctionComponent }
|
|
||||||
)[group.octicon] as React.FunctionComponent
|
if (!octicon) {
|
||||||
|
throw new Error(`Octicon ${group.octicon} not found`)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mr-2">
|
<div className="mr-2">
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function createTranslationFallbackComment(error: Error, property: string)
|
|||||||
} else if (isAutotitleError(error)) {
|
} else if (isAutotitleError(error)) {
|
||||||
// For AUTOTITLE errors, include the error message
|
// For AUTOTITLE errors, include the error message
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
let cleanMessage = error.message
|
const cleanMessage = error.message
|
||||||
.replace(/\n/g, ' ')
|
.replace(/\n/g, ' ')
|
||||||
.replace(/\s+/g, ' ')
|
.replace(/\s+/g, ' ')
|
||||||
.trim()
|
.trim()
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ async function indexOfLearningTrackGuide(
|
|||||||
const renderOpts = { textOnly: true }
|
const renderOpts = { textOnly: true }
|
||||||
for (let i = 0; i < trackGuidePaths.length; i++) {
|
for (let i = 0; i < trackGuidePaths.length; i++) {
|
||||||
// Learning track URLs may have Liquid conditionals.
|
// Learning track URLs may have Liquid conditionals.
|
||||||
let renderedGuidePath = await executeWithFallback(
|
const renderedGuidePath = await executeWithFallback(
|
||||||
context,
|
context,
|
||||||
() => renderContent(trackGuidePaths[i], context, renderOpts),
|
() => renderContent(trackGuidePaths[i], context, renderOpts),
|
||||||
() => '', // todo use english trackGuidePaths[i]
|
() => '', // todo use english trackGuidePaths[i]
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ async function main(opts: MainOptions, args: string[]) {
|
|||||||
'utf-8',
|
'utf-8',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const brokenLinks: {}[] = []
|
const brokenLinks: {
|
||||||
|
linkPath: string
|
||||||
|
file: string
|
||||||
|
}[] = []
|
||||||
|
|
||||||
// Break up the long list of URLs to test into batches
|
// Break up the long list of URLs to test into batches
|
||||||
for (const batch of [...Array(Math.floor(docsLinksFiles.length / BATCH_SIZE)).keys()]) {
|
for (const batch of [...Array(Math.floor(docsLinksFiles.length / BATCH_SIZE)).keys()]) {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ let cleanPath = getCleanPath(providedPath)
|
|||||||
|
|
||||||
// Get the version
|
// Get the version
|
||||||
let version: string | null = getVersion(cleanPath)
|
let version: string | null = getVersion(cleanPath)
|
||||||
let usingFptOnly = !!options.fptOnly
|
const usingFptOnly = !!options.fptOnly
|
||||||
|
|
||||||
// If the URL does not specify a version, default to all versions unless --fptOnly is passed
|
// If the URL does not specify a version, default to all versions unless --fptOnly is passed
|
||||||
if (version === FREE_PRO_TEAM) {
|
if (version === FREE_PRO_TEAM) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { NextFunction, Response, ErrorRequestHandler } from 'express'
|
import type { NextFunction, Response } from 'express'
|
||||||
|
|
||||||
import FailBot from '../lib/failbot'
|
import FailBot from '../lib/failbot'
|
||||||
import { nextApp } from '@/frame/middleware/next'
|
import { nextApp } from '@/frame/middleware/next'
|
||||||
@@ -50,7 +50,7 @@ function timedOut(req: ExtendedRequest) {
|
|||||||
statsd.increment('middleware.timeout', 1, incrementTags)
|
statsd.increment('middleware.timeout', 1, incrementTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleError: ErrorRequestHandler = async function handleError(
|
async function handleError(
|
||||||
error: ErrorWithCode | number,
|
error: ErrorWithCode | number,
|
||||||
req: ExtendedRequest,
|
req: ExtendedRequest,
|
||||||
res: Response,
|
res: Response,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('getAutomaticRequestLogger', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override res.end to simulate response completion
|
// Override res.end to simulate response completion
|
||||||
const endOverride = function (this: any, chunk?: any, encoding?: any) {
|
function endOverride(this: any, chunk?: any, encoding?: any) {
|
||||||
if (!responseEnded) {
|
if (!responseEnded) {
|
||||||
responseEnded = true
|
responseEnded = true
|
||||||
// Simulate a small delay for response time
|
// Simulate a small delay for response time
|
||||||
@@ -143,7 +143,7 @@ describe('getAutomaticRequestLogger', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override res.end to simulate response completion
|
// Override res.end to simulate response completion
|
||||||
const endOverride = function (this: any, chunk?: any, encoding?: any) {
|
function endOverride(this: any, chunk?: any, encoding?: any) {
|
||||||
if (!responseEnded) {
|
if (!responseEnded) {
|
||||||
responseEnded = true
|
responseEnded = true
|
||||||
// Simulate a small delay for response time
|
// Simulate a small delay for response time
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ function FineGrainedAccess({ progAccess }: FineGrainedProps) {
|
|||||||
// Each object represents a set of permissions containing one
|
// Each object represents a set of permissions containing one
|
||||||
// or more key-value pairs. All permissions in a set are required.
|
// or more key-value pairs. All permissions in a set are required.
|
||||||
// If there is more than one set of permissions, any set can be used.
|
// If there is more than one set of permissions, any set can be used.
|
||||||
const formattedPermissions = progAccess.permissions.map((permissionSet: Object, index) => {
|
const formattedPermissions = progAccess.permissions.map((permissionSet: object, index) => {
|
||||||
// Given the example above, the first object is now an array of tuples
|
// Given the example above, the first object is now an array of tuples
|
||||||
// [['"Actions" repository permissions', 'read'], ['"Administration" organization permissions', 'read']]
|
// [['"Actions" repository permissions', 'read'], ['"Administration" organization permissions', 'read']]
|
||||||
// that can be formatted as a string like `"Administration" organization permissions (write)'
|
// that can be formatted as a string like `"Administration" organization permissions (write)'
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export interface ProgAccessT {
|
|||||||
userToServerRest?: boolean
|
userToServerRest?: boolean
|
||||||
serverToServer?: boolean
|
serverToServer?: boolean
|
||||||
fineGrainedPat?: boolean
|
fineGrainedPat?: boolean
|
||||||
permissions: Array<Object>
|
permissions: Array<object>
|
||||||
allowPermissionlessAccess?: boolean
|
allowPermissionlessAccess?: boolean
|
||||||
allowsPublicRead?: boolean
|
allowsPublicRead?: boolean
|
||||||
basicAuth?: boolean
|
basicAuth?: boolean
|
||||||
@@ -90,8 +90,8 @@ export type ExampleT = {
|
|||||||
statusCode: string
|
statusCode: string
|
||||||
contentType?: string
|
contentType?: string
|
||||||
description: string
|
description: string
|
||||||
example?: Object
|
example?: object
|
||||||
schema?: Object
|
schema?: object
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
|||||||
req.context,
|
req.context,
|
||||||
)) as MinitocItemsT
|
)) as MinitocItemsT
|
||||||
|
|
||||||
restOperationsMiniTocItems && miniTocItems.push(...restOperationsMiniTocItems)
|
if (restOperationsMiniTocItems) {
|
||||||
|
miniTocItems.push(...restOperationsMiniTocItems)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the toc items in the context with the REST toc items we just
|
// Replace the toc items in the context with the REST toc items we just
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
|||||||
req.context,
|
req.context,
|
||||||
)) as MinitocItemsT
|
)) as MinitocItemsT
|
||||||
|
|
||||||
restOperationsMiniTocItems && miniTocItems.push(...restOperationsMiniTocItems)
|
if (restOperationsMiniTocItems) {
|
||||||
|
miniTocItems.push(...restOperationsMiniTocItems)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainContext = await getMainContext(req, res)
|
const mainContext = await getMainContext(req, res)
|
||||||
|
|||||||
@@ -98,9 +98,11 @@ async function createCheckContentDirectory(contentFiles: string[]): Promise<Chec
|
|||||||
})
|
})
|
||||||
|
|
||||||
allCompleteVersions.forEach((version) => {
|
allCompleteVersions.forEach((version) => {
|
||||||
!checkContent[version][category]
|
if (!checkContent[version][category]) {
|
||||||
? (checkContent[version][category] = [subCategory])
|
checkContent[version][category] = [subCategory]
|
||||||
: checkContent[version][category].push(subCategory)
|
} else {
|
||||||
|
checkContent[version][category].push(subCategory)
|
||||||
|
}
|
||||||
checkContent[version][category].sort()
|
checkContent[version][category].sort()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -121,9 +123,11 @@ function getOnlyApiVersions(version: string): string[] {
|
|||||||
function createCheckObj(): CheckObject {
|
function createCheckObj(): CheckObject {
|
||||||
const versions: CheckObject = {}
|
const versions: CheckObject = {}
|
||||||
Object.keys(allVersions).forEach((version) => {
|
Object.keys(allVersions).forEach((version) => {
|
||||||
isApiVersioned(version)
|
if (isApiVersioned(version)) {
|
||||||
? getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
|
getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
|
||||||
: (versions[`${allVersions[version].version}`] = {})
|
} else {
|
||||||
|
versions[`${allVersions[version].version}`] = {}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return versions
|
return versions
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ async function validateInputParameters(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that the source repo exists.
|
// Check that the source repo exists.
|
||||||
for (let sourceRepoDirectory of sourceRepoDirectories) {
|
for (const sourceRepoDirectory of sourceRepoDirectories) {
|
||||||
if (!existsSync(sourceRepoDirectory)) {
|
if (!existsSync(sourceRepoDirectory)) {
|
||||||
const errorMsg =
|
const errorMsg =
|
||||||
sourceRepoDirectory === 'github' || sourceRepoDirectory === GITHUB_REP_DIR
|
sourceRepoDirectory === 'github' || sourceRepoDirectory === GITHUB_REP_DIR
|
||||||
|
|||||||
@@ -77,13 +77,13 @@ export async function injectModelsSchema(schema: any, schemaName: string): Promi
|
|||||||
tags: operationObject.tags || ['models'], // Only use 'models' if no tags present
|
tags: operationObject.tags || ['models'], // Only use 'models' if no tags present
|
||||||
verb: operation,
|
verb: operation,
|
||||||
requestPath: path,
|
requestPath: path,
|
||||||
category: category,
|
category,
|
||||||
subcategory: operationObject['x-github']?.subcategory || '',
|
subcategory: operationObject['x-github']?.subcategory || '',
|
||||||
summary: name,
|
summary: name,
|
||||||
description: description,
|
description,
|
||||||
'x-github': {
|
'x-github': {
|
||||||
...operationObject['x-github'], // Preserve all x-github metadata
|
...operationObject['x-github'], // Preserve all x-github metadata
|
||||||
category: category,
|
category,
|
||||||
enabledForGitHubApps: operationObject['x-github']?.enabledForGitHubApps,
|
enabledForGitHubApps: operationObject['x-github']?.enabledForGitHubApps,
|
||||||
githubCloudOnly: operationObject['x-github']?.githubCloudOnly,
|
githubCloudOnly: operationObject['x-github']?.githubCloudOnly,
|
||||||
permissions: operationObject['x-github']?.permissions || {},
|
permissions: operationObject['x-github']?.permissions || {},
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ export async function executeCombinedSearch(
|
|||||||
debug = false,
|
debug = false,
|
||||||
abortSignal?: AbortSignal,
|
abortSignal?: AbortSignal,
|
||||||
) {
|
) {
|
||||||
let language = router.locale || 'en'
|
const language = router.locale || 'en'
|
||||||
|
|
||||||
const params = new URLSearchParams({ query: query, version, language })
|
const params = new URLSearchParams({ query, version, language })
|
||||||
if (debug) {
|
if (debug) {
|
||||||
params.set('debug', '1')
|
params.set('debug', '1')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const DEBOUNCE_TIME = 100 // In milliseconds
|
|||||||
|
|
||||||
// Results are only cached for the current session
|
// Results are only cached for the current session
|
||||||
// We cache results so if a user presses backspace, we can show the results immediately without burdening the API
|
// We cache results so if a user presses backspace, we can show the results immediately without burdening the API
|
||||||
let sessionCache = {} as Record<string, SearchOptions>
|
const sessionCache = {} as Record<string, SearchOptions>
|
||||||
|
|
||||||
// Helper to incorporate version & locale into the cache key
|
// Helper to incorporate version & locale into the cache key
|
||||||
function getCacheKey(query: string, version: string, locale: string) {
|
function getCacheKey(query: string, version: string, locale: string) {
|
||||||
|
|||||||
@@ -45,9 +45,17 @@ export function useMultiQueryParams() {
|
|||||||
const searchParams = new URLSearchParams(asPathQuery)
|
const searchParams = new URLSearchParams(asPathQuery)
|
||||||
initialKeys.forEach((key) => {
|
initialKeys.forEach((key) => {
|
||||||
if (key === 'search-overlay-ask-ai') {
|
if (key === 'search-overlay-ask-ai') {
|
||||||
newParams[key] === 'true' ? searchParams.set(key, 'true') : searchParams.delete(key)
|
if (newParams[key] === 'true') {
|
||||||
|
searchParams.set(key, 'true')
|
||||||
|
} else {
|
||||||
|
searchParams.delete(key)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newParams[key] ? searchParams.set(key, newParams[key]) : searchParams.delete(key)
|
if (newParams[key]) {
|
||||||
|
searchParams.set(key, newParams[key])
|
||||||
|
} else {
|
||||||
|
searchParams.delete(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const paramsString = searchParams.toString() ? `?${searchParams.toString()}` : ''
|
const paramsString = searchParams.toString() ? `?${searchParams.toString()}` : ''
|
||||||
|
|||||||
@@ -566,6 +566,6 @@ function sendAISearchResultEvent({
|
|||||||
ai_search_result_response_status: status,
|
ai_search_result_response_status: status,
|
||||||
ai_search_result_connected_event_id: connectedEventId,
|
ai_search_result_connected_event_id: connectedEventId,
|
||||||
eventGroupKey: ASK_AI_EVENT_GROUP,
|
eventGroupKey: ASK_AI_EVENT_GROUP,
|
||||||
eventGroupId: eventGroupId,
|
eventGroupId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ export function SearchOverlay({
|
|||||||
if (searchParams.has('query')) {
|
if (searchParams.has('query')) {
|
||||||
searchParams.delete('query')
|
searchParams.delete('query')
|
||||||
}
|
}
|
||||||
router.push(`${selectedOption.url}?${searchParams.toString()}` || '')
|
router.push(`${selectedOption.url}?${searchParams.toString()}`)
|
||||||
onClose()
|
onClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,12 +412,12 @@ export function SearchOverlay({
|
|||||||
if (searchParams.has('query')) {
|
if (searchParams.has('query')) {
|
||||||
searchParams.delete('query')
|
searchParams.delete('query')
|
||||||
}
|
}
|
||||||
window.open(`${url}?${searchParams.toString()}` || '', '_blank')
|
window.open(`${url}?${searchParams.toString()}`, '_blank')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle keyboard navigation of suggestions
|
// Handle keyboard navigation of suggestions
|
||||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||||
let optionsLength = listElementsRef.current?.length ?? 0
|
const optionsLength = listElementsRef.current?.length ?? 0
|
||||||
if (event.key === 'ArrowDown') {
|
if (event.key === 'ArrowDown') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (optionsLength > 0) {
|
if (optionsLength > 0) {
|
||||||
@@ -855,8 +855,8 @@ function renderSearchGroups(
|
|||||||
) {
|
) {
|
||||||
const groups = []
|
const groups = []
|
||||||
|
|
||||||
let isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
|
const isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
|
||||||
let isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
|
const isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
|
||||||
|
|
||||||
// This spinner is for both the AI search and the general search results.
|
// This spinner is for both the AI search and the general search results.
|
||||||
// We already show a spinner when streaming AI response, so don't want to show 2 here
|
// We already show a spinner when streaming AI response, so don't want to show 2 here
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export async function getAISearchAutocompleteResults({
|
|||||||
const t0 = new Date()
|
const t0 = new Date()
|
||||||
const client = getElasticsearchClient() as Client
|
const client = getElasticsearchClient() as Client
|
||||||
|
|
||||||
let searchQuery: any = {
|
const searchQuery: any = {
|
||||||
index: indexName,
|
index: indexName,
|
||||||
size,
|
size,
|
||||||
// Send absolutely minimal from Elasticsearch to here. Less data => faster.
|
// Send absolutely minimal from Elasticsearch to here. Less data => faster.
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ router.get(
|
|||||||
// If no query is provided, we want to return the top 5 most popular terms
|
// If no query is provided, we want to return the top 5 most popular terms
|
||||||
// This is a special case for AI search autocomplete
|
// This is a special case for AI search autocomplete
|
||||||
// So we use `force` to allow the query to be empty without the usual validation error
|
// So we use `force` to allow the query to be empty without the usual validation error
|
||||||
let force = {} as any
|
const force = {} as any
|
||||||
if (!req.query.query) {
|
if (!req.query.query) {
|
||||||
force.query = ''
|
force.query = ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ export async function populateIndex(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (bulkResponse.errors) {
|
if (bulkResponse.failed > 0) {
|
||||||
console.error(`Bulk response errors: ${bulkResponse.errors}`)
|
console.error(`Bulk response failed: ${bulkResponse.failed} documents failed`)
|
||||||
throw new Error('Bulk errors happened.')
|
throw new Error('Bulk errors happened.')
|
||||||
}
|
}
|
||||||
const t1 = new Date()
|
const t1 = new Date()
|
||||||
@@ -140,7 +140,7 @@ export async function updateAlias(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (const index of indices) {
|
for (const index of indices) {
|
||||||
if (index.index !== indexAlias && index.index.startsWith(indexName)) {
|
if (index.index && index.index !== indexAlias && index.index.startsWith(indexName)) {
|
||||||
aliasUpdates.push({ remove_index: { index: index.index } })
|
aliasUpdates.push({ remove_index: { index: index.index } })
|
||||||
console.log('Deleting old index', index.index)
|
console.log('Deleting old index', index.index)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
|
|
||||||
import { sleep } from '@/search/lib/helpers/time'
|
import { sleep } from '@/search/lib/helpers/time'
|
||||||
|
|
||||||
export async function retryOnErrorTest(
|
export async function retryOnErrorTest<T>(
|
||||||
errorTest: (error: any) => boolean,
|
errorTest: (error: unknown) => boolean,
|
||||||
callback: Function,
|
callback: () => Promise<T>,
|
||||||
{
|
{
|
||||||
attempts = 4,
|
attempts = 4,
|
||||||
sleepTime = 1000,
|
sleepTime = 1000,
|
||||||
@@ -37,7 +37,7 @@ export async function retryOnErrorTest(
|
|||||||
jitterPercent?: number
|
jitterPercent?: number
|
||||||
onError?: (error: Error, attempts: number, sleepTime: number) => void
|
onError?: (error: Error, attempts: number, sleepTime: number) => void
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
): Promise<T> {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
return await callback()
|
return await callback()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ describe('AI Search Routes', () => {
|
|||||||
afterAll(() => stopMockServer())
|
afterAll(() => stopMockServer())
|
||||||
|
|
||||||
test('/api/ai-search/v1 should handle a successful response', async () => {
|
test('/api/ai-search/v1 should handle a successful response', async () => {
|
||||||
let apiBody = { query: 'How do I create a Repository?', language: 'en', version: 'dotcom' }
|
const apiBody = { query: 'How do I create a Repository?', language: 'en', version: 'dotcom' }
|
||||||
|
|
||||||
const response = await fetch('http://localhost:4000/api/ai-search/v1', {
|
const response = await fetch('http://localhost:4000/api/ai-search/v1', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -80,7 +80,7 @@ describe('AI Search Routes', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('should handle validation errors: query missing', async () => {
|
test('should handle validation errors: query missing', async () => {
|
||||||
let body = { language: 'en', version: 'dotcom' }
|
const body = { language: 'en', version: 'dotcom' }
|
||||||
const response = await post('/api/ai-search/v1', {
|
const response = await post('/api/ai-search/v1', {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -95,7 +95,7 @@ describe('AI Search Routes', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('should handle validation errors: version missing', async () => {
|
test('should handle validation errors: version missing', async () => {
|
||||||
let body = { query: 'example query' }
|
const body = { query: 'example query' }
|
||||||
const response = await post('/api/ai-search/v1', {
|
const response = await post('/api/ai-search/v1', {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -110,7 +110,7 @@ describe('AI Search Routes', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('should handle multiple validation errors: query missing and version', async () => {
|
test('should handle multiple validation errors: query missing and version', async () => {
|
||||||
let body = { language: 'fr', version: 'fpt' }
|
const body = { language: 'fr', version: 'fpt' }
|
||||||
const response = await post('/api/ai-search/v1', {
|
const response = await post('/api/ai-search/v1', {
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -140,7 +140,7 @@ describe('AI Search Routes', () => {
|
|||||||
if (response.body) {
|
if (response.body) {
|
||||||
const reader = response.body.getReader()
|
const reader = response.body.getReader()
|
||||||
const decoder = new TextDecoder()
|
const decoder = new TextDecoder()
|
||||||
let chunks = []
|
const chunks = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export type Context = {
|
|||||||
enPage?: Page
|
enPage?: Page
|
||||||
productNames?: ProductNames
|
productNames?: ProductNames
|
||||||
currentVersion?: string
|
currentVersion?: string
|
||||||
process?: { env: {} }
|
process?: { env: Record<string, string> }
|
||||||
site?: {
|
site?: {
|
||||||
data: {
|
data: {
|
||||||
ui: any
|
ui: any
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ main().then(
|
|||||||
// Using any for token objects as liquidjs doesn't provide TypeScript types
|
// Using any for token objects as liquidjs doesn't provide TypeScript types
|
||||||
function removeInputProps(arrayOfObjects: any[]): any[] {
|
function removeInputProps(arrayOfObjects: any[]): any[] {
|
||||||
return arrayOfObjects.map((obj: any) => {
|
return arrayOfObjects.map((obj: any) => {
|
||||||
delete obj.input || delete obj.token.input
|
delete obj.input
|
||||||
|
delete obj.token.input
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface WebhookActionData {
|
|||||||
codeExamples: Array<CodeSample>
|
codeExamples: Array<CodeSample>
|
||||||
availability: Array<string>
|
availability: Array<string>
|
||||||
action: string
|
action: string
|
||||||
payloadExample?: Object
|
payloadExample?: object
|
||||||
}
|
}
|
||||||
export interface WebhookAction {
|
export interface WebhookAction {
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
|||||||
webhooks.map((webhook) => webhook.data.category),
|
webhooks.map((webhook) => webhook.data.category),
|
||||||
context,
|
context,
|
||||||
)
|
)
|
||||||
webhooksMiniTocs && miniTocItems.push(...webhooksMiniTocs)
|
if (webhooksMiniTocs) {
|
||||||
|
miniTocItems.push(...webhooksMiniTocs)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
import * as github from '@actions/github'
|
import github from '@actions/github'
|
||||||
import core from '@actions/core'
|
import core from '@actions/core'
|
||||||
|
|
||||||
import walk from 'walk-sync'
|
import walk from 'walk-sync'
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ async function main() {
|
|||||||
console.log('ID:', issue.id)
|
console.log('ID:', issue.id)
|
||||||
console.log('Number:', issue.number)
|
console.log('Number:', issue.number)
|
||||||
console.log('URL:', issue.html_url)
|
console.log('URL:', issue.html_url)
|
||||||
number = issue.number
|
number = String(issue.number)
|
||||||
if (number) {
|
if (number) {
|
||||||
// We've found the issue (pull request), but before we accept
|
// We've found the issue (pull request), but before we accept
|
||||||
// this `number`, check that the issue isn't locked.
|
// this `number`, check that the issue isn't locked.
|
||||||
|
|||||||
@@ -234,12 +234,12 @@ async function searchCode(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function secondaryRateLimitRetry(
|
async function secondaryRateLimitRetry<T, TArgs = Record<string, unknown>>(
|
||||||
callable: Function,
|
callable: (args: TArgs) => Promise<T>,
|
||||||
args: Record<string, any>,
|
args: TArgs,
|
||||||
maxAttempts = 10,
|
maxAttempts = 10,
|
||||||
sleepTime = 1000,
|
sleepTime = 1000,
|
||||||
) {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const response = await callable(args)
|
const response = await callable(args)
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ const DEFAULT_ATTEMPTS = parseInt(process.env.SECONDARY_RATELIMIT_RETRY_ATTEMPTS
|
|||||||
// "You have exceeded a secondary rate limit".
|
// "You have exceeded a secondary rate limit".
|
||||||
// More info about what they are here:
|
// More info about what they are here:
|
||||||
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits
|
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits
|
||||||
export async function octoSecondaryRatelimitRetry(
|
export async function octoSecondaryRatelimitRetry<T>(
|
||||||
fn: Function,
|
fn: () => Promise<T>,
|
||||||
{ attempts = DEFAULT_ATTEMPTS, sleepTime = DEFAULT_SLEEPTIME } = {},
|
{ attempts = DEFAULT_ATTEMPTS, sleepTime = DEFAULT_SLEEPTIME } = {},
|
||||||
) {
|
): Promise<T> {
|
||||||
let tries = 0
|
let tries = 0
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -25,11 +25,17 @@ export function readFiles(dir = 'content', ext = 'md', opts = {}) {
|
|||||||
return paths.map((path) => [path, fs.readFileSync(path, 'utf8')])
|
return paths.map((path) => [path, fs.readFileSync(path, 'utf8')])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterFiles(files: [path: string, file: string][], fn: Function) {
|
export function filterFiles(
|
||||||
|
files: [path: string, file: string][],
|
||||||
|
fn: (path: string, file: string) => boolean,
|
||||||
|
) {
|
||||||
return files.filter(([path, file]) => fn(path, file))
|
return files.filter(([path, file]) => fn(path, file))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withFiles(files: [path: string, file: string][], fn: Function) {
|
export function withFiles(
|
||||||
|
files: [path: string, file: string][],
|
||||||
|
fn: (path: string, file: string) => string,
|
||||||
|
) {
|
||||||
return files.map(([path, file]) => [path, fn(path, file)])
|
return files.map(([path, file]) => [path, fn(path, file)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user