Eslint review, round 1 (#58168)
This commit is contained in:
@@ -67,63 +67,33 @@ export default [
|
||||
// JSX A11y recommended rules
|
||||
...jsxA11y.configs.recommended.rules,
|
||||
|
||||
// TypeScript-specific overrides
|
||||
// Overrides
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
packageDir: '.',
|
||||
},
|
||||
],
|
||||
'import/extensions': 'off',
|
||||
'no-console': 'off',
|
||||
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
|
||||
'import/extensions': ['error', { json: 'always' }],
|
||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'prefer-const': ['error', { destructuring: 'all' }],
|
||||
|
||||
// Disable GitHub plugin rules that were disabled in original config
|
||||
'github/array-foreach': 'off',
|
||||
'github/no-then': 'off',
|
||||
// Rules that must be disabled
|
||||
'no-redeclare': 'off', // Allow function overloads in TypeScript
|
||||
'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
|
||||
'i18n-text/no-en': 'off',
|
||||
'filenames/match-regex': 'off',
|
||||
'eslint-comments/no-use': 'off',
|
||||
'eslint-comments/no-unused-disable': 'off',
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
|
||||
// Disable new ESLint 9 rules that are causing issues
|
||||
'no-constant-binary-expression': 'off',
|
||||
|
||||
// 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',
|
||||
// Disabled rules to review
|
||||
'github/no-then': 'off', // 30+
|
||||
'@typescript-eslint/ban-ts-comment': 'off', // 50+
|
||||
'no-undef': 'off', // 50+
|
||||
'no-shadow': 'off', // 150+
|
||||
'prefer-template': 'off', // 150+
|
||||
'github/array-foreach': 'off', // 250+
|
||||
camelcase: 'off', // 600+
|
||||
'no-console': 'off', // 800+
|
||||
'@typescript-eslint/no-explicit-any': 'off', // 1000+
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
14
package.json
14
package.json
@@ -53,7 +53,7 @@
|
||||
"index-general-search": "tsx src/search/scripts/index/index-cli general-search",
|
||||
"index-test-fixtures": "./src/search/scripts/index-test-fixtures.sh",
|
||||
"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-translation": "vitest src/content-linter/tests/lint-files.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",
|
||||
"precompute-pageinfo": "tsx src/article-api/scripts/precompute-pageinfo.ts",
|
||||
"prepare": "husky src/workflows/husky",
|
||||
"prettier": "prettier -w \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
|
||||
"prettier-check": "prettier -c \"**/*.{ts,tsx,js,mjs,scss,yml,yaml}\"",
|
||||
"prettier": "prettier -w \"**/*.{ts,tsx,scss,yml,yaml}\"",
|
||||
"prettier-check": "prettier -c \"**/*.{ts,tsx,scss,yml,yaml}\"",
|
||||
"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-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"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,mjs,ts,tsx}": "eslint --cache --fix",
|
||||
"*.{js,mjs,scss,ts,tsx,yml,yaml}": "prettier --write",
|
||||
"*.{ts,tsx}": "eslint --cache --fix",
|
||||
"*.{scss,ts,tsx,yml,yaml}": "prettier --write",
|
||||
"{content,data}/**/*.md": "npm run lint-content -- --precommit --paths"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"ext": "ts,js,json,yml,md,html,scss",
|
||||
"ext": "ts,json,yml,md,html,scss",
|
||||
"exec": "tsx",
|
||||
"ignore": [
|
||||
"assets",
|
||||
@@ -139,7 +139,7 @@
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"**/*.{ts,tsx,js,mjs}"
|
||||
"**/*.{ts,tsx}"
|
||||
],
|
||||
"options": {
|
||||
"printWidth": 100,
|
||||
|
||||
@@ -235,7 +235,7 @@ export default async function archivedEnterpriseVersions(
|
||||
// `x-host` is a custom header set by Fastly.
|
||||
// GLB automatically deletes the `x-forwarded-host` header.
|
||||
const host = req.get('x-host') || req.get('x-forwarded-host') || req.get('host')
|
||||
let modifiedBody = body
|
||||
const modifiedBody = body
|
||||
.replaceAll(
|
||||
`${OLD_AZURE_BLOB_ENTERPRISE_DIR}/${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'
|
||||
|
||||
const { GITHUB_TOKEN } = process.env
|
||||
|
||||
@@ -145,7 +145,9 @@ async function main(opts: MainOptions) {
|
||||
// Add exceptions
|
||||
sourceFiles.push('.github/CONTRIBUTING.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(
|
||||
walk(
|
||||
@@ -159,7 +161,9 @@ async function main(opts: MainOptions) {
|
||||
).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) {
|
||||
const content = fs.readFileSync(sourceFile, 'utf-8')
|
||||
|
||||
@@ -43,7 +43,7 @@ type MockResponse = {
|
||||
_json?: string
|
||||
_send?: string
|
||||
headers: Record<string, string>
|
||||
set?: (key: string | Object, value: string) => void
|
||||
set?: (key: string | object, value: string) => void
|
||||
removeHeader?: (key: string) => void
|
||||
hasHeader?: (key: string) => boolean
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ async function resolveReferenceLinksToTitles(
|
||||
// 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 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))
|
||||
}
|
||||
|
||||
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
|
||||
// "N/A"), then we don't show the event.
|
||||
const filteredAuditLogEvents = auditLogEvents.filter(
|
||||
const filteredAuditLogEvents = auditLogEvents?.filter(
|
||||
(event) => event.description !== 'N/A' && event.description !== '',
|
||||
)
|
||||
|
||||
return filteredAuditLogEvents
|
||||
return filteredAuditLogEvents || []
|
||||
}
|
||||
|
||||
// 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] ],
|
||||
// user: [ [Object], [Object] ]
|
||||
// }
|
||||
export function getCategorizedAuditLogEvents(page: string, version: string) {
|
||||
export function getCategorizedAuditLogEvents(page: string, version: string): CategorizedEvents {
|
||||
const events = getAuditLogEvents(page, version)
|
||||
const openApiVersion = getOpenApiVersion(version)
|
||||
|
||||
@@ -148,7 +148,7 @@ export function getCategorizedAuditLogEvents(page: string, version: string) {
|
||||
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.
|
||||
|
||||
@@ -86,7 +86,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
||||
Object.keys(auditLogEvents).map((category) => category),
|
||||
context,
|
||||
)
|
||||
auditLogEventsMiniTocs && miniTocItems.push(...auditLogEventsMiniTocs)
|
||||
if (auditLogEventsMiniTocs) {
|
||||
miniTocItems.push(...auditLogEventsMiniTocs)
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as nodePath from 'path'
|
||||
import nodePath from 'path'
|
||||
import { reportingConfig } from '@/content-linter/style/github-docs'
|
||||
|
||||
interface LintFlaw {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const liquidIfversionVersions = {
|
||||
// The versions frontmatter object or all versions if the file
|
||||
// being processed is a data file.
|
||||
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')
|
||||
? { ghec: '*', ghes: '*', fpt: '*' }
|
||||
|
||||
@@ -72,7 +72,7 @@ export const noteWarningFormatting = {
|
||||
|
||||
// Collect content inside legacy notes
|
||||
if (inLegacyNote) {
|
||||
noteContent.push({ text: line, lineNumber: lineNumber })
|
||||
noteContent.push({ text: line, lineNumber })
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ function findOutdatedTerminologyMatches(line: string): MatchInfo[] {
|
||||
start: match.index,
|
||||
end: match.index + match[0].length,
|
||||
text: match[0],
|
||||
replacement: replacement,
|
||||
outdatedTerm: outdatedTerm,
|
||||
replacement,
|
||||
outdatedTerm,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +240,9 @@ if (ymlToLint.length === 0) {
|
||||
describe('lint yaml content', () => {
|
||||
if (ymlToLint.length < 1) return
|
||||
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.
|
||||
// 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.
|
||||
|
||||
@@ -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 %},
|
||||
// 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.
|
||||
export default class extends Tag {
|
||||
export default class Ifversion extends Tag {
|
||||
tagToken: TagToken
|
||||
branches: Branch[]
|
||||
elseTemplates: Template[]
|
||||
|
||||
@@ -29,7 +29,7 @@ export const Prompt: LiquidTag = {
|
||||
},
|
||||
|
||||
// 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)
|
||||
|
||||
// build a URL with the prompt text encoded as query parameter
|
||||
|
||||
@@ -53,7 +53,7 @@ export const Spotlight = {
|
||||
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)
|
||||
|
||||
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
|
||||
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)
|
||||
return yield this.liquid.parseAndRender(template, {
|
||||
tagName: this.tagName,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import * as github from '@actions/github'
|
||||
import github from '@actions/github'
|
||||
import core from '@actions/core'
|
||||
import { program } from 'commander'
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('data-directory', () => {
|
||||
})
|
||||
|
||||
test('option: preprocess function', async () => {
|
||||
const preprocess = function (content: string) {
|
||||
function preprocess(content: string) {
|
||||
return content.replace('markdown', 'MARKDOWN')
|
||||
}
|
||||
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
|
||||
const getValidationErrorHash = (validateErrors: ErrorObject[]) => {
|
||||
// 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 || [])
|
||||
.map((error: ErrorObject) => error.message + error.instancePath + JSON.stringify(error.params))
|
||||
.join(':')}`
|
||||
|
||||
@@ -105,12 +105,12 @@ export type EventPropsByType = {
|
||||
link_samepage?: boolean
|
||||
link_container?: string
|
||||
}
|
||||
[EventType.page]: {}
|
||||
[EventType.page]: { type: string } // no unique properties
|
||||
[EventType.preference]: {
|
||||
preference_name: string
|
||||
preference_value: string
|
||||
}
|
||||
[EventType.print]: {}
|
||||
[EventType.print]: { type: string } // no unique properties
|
||||
[EventType.search]: {
|
||||
search_query: string
|
||||
search_context?: string
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('annotations', () => {
|
||||
const noteTexts = notes.map((i: number, el: any) => $(el).text()).get()
|
||||
expect(noteTexts).toEqual([
|
||||
'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(() => {
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
configurable: true,
|
||||
get: function () {
|
||||
get() {
|
||||
return 'hidden'
|
||||
},
|
||||
})
|
||||
@@ -803,7 +803,7 @@ test.describe('survey', () => {
|
||||
await page.evaluate(() => {
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
configurable: true,
|
||||
get: function () {
|
||||
get() {
|
||||
return 'hidden'
|
||||
},
|
||||
})
|
||||
|
||||
@@ -24,8 +24,8 @@ export const SharedUIContextProvider = ({ children }: { children: React.ReactNod
|
||||
return (
|
||||
<SharedUIContext.Provider
|
||||
value={{
|
||||
hasOpenHeaderNotifications: hasOpenHeaderNotifications,
|
||||
setHasOpenHeaderNotifications: setHasOpenHeaderNotifications,
|
||||
hasOpenHeaderNotifications,
|
||||
setHasOpenHeaderNotifications,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMainContext } from '@/frame/components/context/MainContext'
|
||||
|
||||
export type FeatureFlags = {}
|
||||
export type FeatureFlags = Record<string, string>
|
||||
|
||||
export const useFeatureFlags = (): FeatureFlags => {
|
||||
const { featureFlags } = useMainContext()
|
||||
|
||||
@@ -86,8 +86,8 @@ export const UnrenderedMarkdownContent = ({
|
||||
sendEvent({
|
||||
type: EventType.clipboard,
|
||||
clipboard_operation: 'copy',
|
||||
eventGroupKey: eventGroupKey,
|
||||
eventGroupId: eventGroupId,
|
||||
eventGroupKey,
|
||||
eventGroupId,
|
||||
})
|
||||
}}
|
||||
></IconButton>
|
||||
|
||||
@@ -11,7 +11,8 @@ function readFrontmatter(markdown: string, opts: ReadFrontmatterOptions = {}) {
|
||||
const schema = opts.schema || { type: 'object', properties: {} }
|
||||
const filepath = opts.filepath || null
|
||||
|
||||
let content, data
|
||||
let content
|
||||
let data
|
||||
|
||||
try {
|
||||
;({ content, data } = matter(markdown))
|
||||
|
||||
@@ -5,9 +5,18 @@ import { createLogger } from '@/observability/logger'
|
||||
|
||||
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
|
||||
// it's wrapped in a timer that reports to Datadog
|
||||
const dog: Record<string, Function> = {
|
||||
const dog = {
|
||||
loadUnversionedTree: statsd.asyncTimer(loadUnversionedTree, 'load_unversioned_tree'),
|
||||
loadSiteTree: statsd.asyncTimer(loadSiteTree, 'load_site_tree'),
|
||||
loadPages: statsd.asyncTimer(loadPages, 'load_pages'),
|
||||
@@ -17,9 +26,9 @@ const dog: Record<string, Function> = {
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
logger.debug(
|
||||
@@ -42,6 +51,7 @@ async function warmServer(languagesOnly = []) {
|
||||
unversionedTree,
|
||||
siteTree,
|
||||
pageList,
|
||||
pageMap,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ if (process.env.ELASTICSEARCH_URL) {
|
||||
createProxyMiddleware({
|
||||
target: 'https://docs.github.com',
|
||||
changeOrigin: true,
|
||||
pathRewrite: function (path, req: ExtendedRequest) {
|
||||
pathRewrite(path, req: ExtendedRequest) {
|
||||
return req.originalUrl
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -23,7 +23,7 @@ export function blockIndex(path: string) {
|
||||
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')
|
||||
return next()
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ async function getTocItems(node: Tree, context: Context, opts: Options): Promise
|
||||
}
|
||||
}
|
||||
|
||||
let childTocItems = []
|
||||
const childTocItems = []
|
||||
if (child.childPages) {
|
||||
childTocItems.push(...(await getTocItems(child, context, opts)))
|
||||
}
|
||||
|
||||
@@ -18,9 +18,7 @@ const DEFAULT_OPTIONS = {
|
||||
crossOriginResourcePolicy: true,
|
||||
crossOriginEmbedderPolicy: false, // doesn't work with youtube
|
||||
referrerPolicy: {
|
||||
// See docs-engineering #2426
|
||||
// The `... as 'no-referrer-when-downgrade'` is a workaround for TypeScript
|
||||
policy: 'no-referrer-when-downgrade' as 'no-referrer-when-downgrade',
|
||||
policy: 'no-referrer-when-downgrade' as const,
|
||||
},
|
||||
// This module defines a Content Security Policy (CSP) to disallow
|
||||
// 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
|
||||
// https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
|
||||
const asyncMiddleware = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next)
|
||||
}
|
||||
const asyncMiddleware =
|
||||
<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 ***
|
||||
if (!isTest) app.use(timeout(MAX_REQUEST_TIMEOUT))
|
||||
app.use(abort)
|
||||
|
||||
@@ -356,7 +356,7 @@ async function isExistingIssue(
|
||||
): Promise<boolean> {
|
||||
const { labels, searchQuery, titleMatch } = opts
|
||||
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) {
|
||||
query += '+' + searchQuery
|
||||
|
||||
@@ -63,10 +63,11 @@ main()
|
||||
async function main() {
|
||||
console.log(`Archiving Enterprise version: ${version}`)
|
||||
|
||||
let pageList: PageList, urls: Array<string>
|
||||
let pageList: PageList
|
||||
let urls: Array<string>
|
||||
if (singlePage) {
|
||||
const pageName = singlePage.trim().startsWith('/') ? singlePage.slice(1) : singlePage
|
||||
const urls = languageKeys
|
||||
urls = languageKeys
|
||||
.map((key) => `/${key}/enterprise-server@${version}/${pageName}`)
|
||||
.map((href) => `${host}${href}`)
|
||||
console.log(`\nScraping HTML for a single page only:\n${urls.join('\n')}\n`)
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
interface ScraperResource {
|
||||
isHtml(): boolean
|
||||
isCss(): boolean
|
||||
getText(): string
|
||||
getFilename(): string
|
||||
encoding: BufferEncoding
|
||||
}
|
||||
|
||||
interface ResourceSavedArgs {
|
||||
resource: ScraperResource
|
||||
}
|
||||
|
||||
export class RewriteAssetPathsPlugin {
|
||||
tempDirectory: string
|
||||
localDev: boolean
|
||||
@@ -12,8 +24,10 @@ export class RewriteAssetPathsPlugin {
|
||||
this.replaceUrl = replaceUrl
|
||||
}
|
||||
|
||||
apply(registerAction: Function) {
|
||||
registerAction('onResourceSaved', async ({ resource }: any) => {
|
||||
apply(
|
||||
registerAction: (event: string, callback: (args: ResourceSavedArgs) => Promise<void>) => void,
|
||||
) {
|
||||
registerAction('onResourceSaved', async ({ resource }: ResourceSavedArgs) => {
|
||||
// Show some activity
|
||||
process.stdout.write('.')
|
||||
|
||||
|
||||
@@ -70,7 +70,9 @@ export async function getAppsServerSideProps(
|
||||
? Object.values(appsItems).map((item: any) => item.displayTitle!)
|
||||
: Object.keys(appsItems)
|
||||
const appMiniToc = await getAutomatedPageMiniTocItems(titles, context)
|
||||
appMiniToc && miniTocItems.push(...appMiniToc)
|
||||
if (appMiniToc) {
|
||||
miniTocItems.push(...appMiniToc)
|
||||
}
|
||||
|
||||
return { currentVersion, appsItems, categoriesWithoutSubcategories }
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ function getDisplayTitle(
|
||||
: `"${sentenceCase(title)}" ` + resourceGroup + ' permissions'
|
||||
: !resourceGroup
|
||||
? sentenceCase(title) + ' permissions'
|
||||
: sentenceCase(resourceGroup) + ' permissions for ' + `"${title}"`
|
||||
: sentenceCase(resourceGroup) + ` permissions for "${title}"`
|
||||
|
||||
return { title, displayTitle }
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
type Props = {
|
||||
mainContext: MainContextT
|
||||
automatedPageContext: AutomatedPageContextT
|
||||
schema: Object
|
||||
schema: object
|
||||
language: string
|
||||
graphqlPageName: string
|
||||
objects?: ObjectT[]
|
||||
|
||||
@@ -28,7 +28,7 @@ const versionsToBuild = Object.keys(allVersions)
|
||||
|
||||
main()
|
||||
|
||||
let allIgnoredChanges = []
|
||||
const allIgnoredChanges = []
|
||||
|
||||
async function main() {
|
||||
for (const version of versionsToBuild) {
|
||||
|
||||
@@ -2,12 +2,38 @@ import type { ProductGroupT } from '@/landings/components/ProductSelections'
|
||||
|
||||
import React from 'react'
|
||||
import { Link } from '@/frame/components/Link'
|
||||
import * as Octicons from '@primer/octicons-react'
|
||||
import { LinkExternalIcon } from '@primer/octicons-react'
|
||||
import {
|
||||
LinkExternalIcon,
|
||||
RocketIcon,
|
||||
CommentDiscussionIcon,
|
||||
CopilotIcon,
|
||||
GearIcon,
|
||||
ShieldLockIcon,
|
||||
DeviceMobileIcon,
|
||||
ProjectIcon,
|
||||
OrganizationIcon,
|
||||
CodeSquareIcon,
|
||||
GlobeIcon,
|
||||
PencilIcon,
|
||||
} from '@primer/octicons-react'
|
||||
|
||||
type ProductSelectionCardProps = {
|
||||
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) => {
|
||||
// Don't display the group if it has no children due to versioning
|
||||
@@ -27,9 +53,11 @@ export const ProductSelectionCard = ({ group }: ProductSelectionCardProps) => {
|
||||
</div>
|
||||
)
|
||||
} else if (group.octicon) {
|
||||
const octicon: React.FunctionComponent = (
|
||||
Octicons as { [name: string]: React.FunctionComponent }
|
||||
)[group.octicon] as React.FunctionComponent
|
||||
const octicon: React.FunctionComponent = octiconMap[group.octicon]
|
||||
|
||||
if (!octicon) {
|
||||
throw new Error(`Octicon ${group.octicon} not found`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mr-2">
|
||||
|
||||
@@ -78,7 +78,7 @@ export function createTranslationFallbackComment(error: Error, property: string)
|
||||
} else if (isAutotitleError(error)) {
|
||||
// For AUTOTITLE errors, include the error message
|
||||
if (error.message) {
|
||||
let cleanMessage = error.message
|
||||
const cleanMessage = error.message
|
||||
.replace(/\n/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
|
||||
@@ -175,7 +175,7 @@ async function indexOfLearningTrackGuide(
|
||||
const renderOpts = { textOnly: true }
|
||||
for (let i = 0; i < trackGuidePaths.length; i++) {
|
||||
// Learning track URLs may have Liquid conditionals.
|
||||
let renderedGuidePath = await executeWithFallback(
|
||||
const renderedGuidePath = await executeWithFallback(
|
||||
context,
|
||||
() => renderContent(trackGuidePaths[i], context, renderOpts),
|
||||
() => '', // todo use english trackGuidePaths[i]
|
||||
|
||||
@@ -180,7 +180,10 @@ async function main(opts: MainOptions, args: string[]) {
|
||||
'utf-8',
|
||||
)
|
||||
}
|
||||
const brokenLinks: {}[] = []
|
||||
const brokenLinks: {
|
||||
linkPath: string
|
||||
file: string
|
||||
}[] = []
|
||||
|
||||
// Break up the long list of URLs to test into batches
|
||||
for (const batch of [...Array(Math.floor(docsLinksFiles.length / BATCH_SIZE)).keys()]) {
|
||||
|
||||
@@ -145,7 +145,7 @@ let cleanPath = getCleanPath(providedPath)
|
||||
|
||||
// Get the version
|
||||
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 (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 { nextApp } from '@/frame/middleware/next'
|
||||
@@ -50,7 +50,7 @@ function timedOut(req: ExtendedRequest) {
|
||||
statsd.increment('middleware.timeout', 1, incrementTags)
|
||||
}
|
||||
|
||||
const handleError: ErrorRequestHandler = async function handleError(
|
||||
async function handleError(
|
||||
error: ErrorWithCode | number,
|
||||
req: ExtendedRequest,
|
||||
res: Response,
|
||||
|
||||
@@ -43,7 +43,7 @@ describe('getAutomaticRequestLogger', () => {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
responseEnded = true
|
||||
// Simulate a small delay for response time
|
||||
@@ -143,7 +143,7 @@ describe('getAutomaticRequestLogger', () => {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
responseEnded = true
|
||||
// Simulate a small delay for response time
|
||||
|
||||
@@ -75,7 +75,7 @@ function FineGrainedAccess({ progAccess }: FineGrainedProps) {
|
||||
// Each object represents a set of permissions containing one
|
||||
// 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.
|
||||
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
|
||||
// [['"Actions" repository permissions', 'read'], ['"Administration" organization permissions', 'read']]
|
||||
// that can be formatted as a string like `"Administration" organization permissions (write)'
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface ProgAccessT {
|
||||
userToServerRest?: boolean
|
||||
serverToServer?: boolean
|
||||
fineGrainedPat?: boolean
|
||||
permissions: Array<Object>
|
||||
permissions: Array<object>
|
||||
allowPermissionlessAccess?: boolean
|
||||
allowsPublicRead?: boolean
|
||||
basicAuth?: boolean
|
||||
@@ -90,8 +90,8 @@ export type ExampleT = {
|
||||
statusCode: string
|
||||
contentType?: string
|
||||
description: string
|
||||
example?: Object
|
||||
schema?: Object
|
||||
example?: object
|
||||
schema?: object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -200,7 +200,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
||||
req.context,
|
||||
)) 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
|
||||
|
||||
@@ -80,7 +80,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
||||
req.context,
|
||||
)) as MinitocItemsT
|
||||
|
||||
restOperationsMiniTocItems && miniTocItems.push(...restOperationsMiniTocItems)
|
||||
if (restOperationsMiniTocItems) {
|
||||
miniTocItems.push(...restOperationsMiniTocItems)
|
||||
}
|
||||
}
|
||||
|
||||
const mainContext = await getMainContext(req, res)
|
||||
|
||||
@@ -98,9 +98,11 @@ async function createCheckContentDirectory(contentFiles: string[]): Promise<Chec
|
||||
})
|
||||
|
||||
allCompleteVersions.forEach((version) => {
|
||||
!checkContent[version][category]
|
||||
? (checkContent[version][category] = [subCategory])
|
||||
: checkContent[version][category].push(subCategory)
|
||||
if (!checkContent[version][category]) {
|
||||
checkContent[version][category] = [subCategory]
|
||||
} else {
|
||||
checkContent[version][category].push(subCategory)
|
||||
}
|
||||
checkContent[version][category].sort()
|
||||
})
|
||||
}
|
||||
@@ -121,9 +123,11 @@ function getOnlyApiVersions(version: string): string[] {
|
||||
function createCheckObj(): CheckObject {
|
||||
const versions: CheckObject = {}
|
||||
Object.keys(allVersions).forEach((version) => {
|
||||
isApiVersioned(version)
|
||||
? getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
|
||||
: (versions[`${allVersions[version].version}`] = {})
|
||||
if (isApiVersioned(version)) {
|
||||
getOnlyApiVersions(version).forEach((apiVersion) => (versions[apiVersion] = {}))
|
||||
} else {
|
||||
versions[`${allVersions[version].version}`] = {}
|
||||
}
|
||||
})
|
||||
|
||||
return versions
|
||||
|
||||
@@ -242,7 +242,7 @@ async function validateInputParameters(): Promise<void> {
|
||||
}
|
||||
|
||||
// Check that the source repo exists.
|
||||
for (let sourceRepoDirectory of sourceRepoDirectories) {
|
||||
for (const sourceRepoDirectory of sourceRepoDirectories) {
|
||||
if (!existsSync(sourceRepoDirectory)) {
|
||||
const errorMsg =
|
||||
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
|
||||
verb: operation,
|
||||
requestPath: path,
|
||||
category: category,
|
||||
category,
|
||||
subcategory: operationObject['x-github']?.subcategory || '',
|
||||
summary: name,
|
||||
description: description,
|
||||
description,
|
||||
'x-github': {
|
||||
...operationObject['x-github'], // Preserve all x-github metadata
|
||||
category: category,
|
||||
category,
|
||||
enabledForGitHubApps: operationObject['x-github']?.enabledForGitHubApps,
|
||||
githubCloudOnly: operationObject['x-github']?.githubCloudOnly,
|
||||
permissions: operationObject['x-github']?.permissions || {},
|
||||
|
||||
@@ -74,9 +74,9 @@ export async function executeCombinedSearch(
|
||||
debug = false,
|
||||
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) {
|
||||
params.set('debug', '1')
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ const DEBOUNCE_TIME = 100 // In milliseconds
|
||||
|
||||
// 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
|
||||
let sessionCache = {} as Record<string, SearchOptions>
|
||||
const sessionCache = {} as Record<string, SearchOptions>
|
||||
|
||||
// Helper to incorporate version & locale into the cache key
|
||||
function getCacheKey(query: string, version: string, locale: string) {
|
||||
|
||||
@@ -45,9 +45,17 @@ export function useMultiQueryParams() {
|
||||
const searchParams = new URLSearchParams(asPathQuery)
|
||||
initialKeys.forEach((key) => {
|
||||
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 {
|
||||
newParams[key] ? searchParams.set(key, newParams[key]) : searchParams.delete(key)
|
||||
searchParams.delete(key)
|
||||
}
|
||||
} else {
|
||||
if (newParams[key]) {
|
||||
searchParams.set(key, newParams[key])
|
||||
} else {
|
||||
searchParams.delete(key)
|
||||
}
|
||||
}
|
||||
})
|
||||
const paramsString = searchParams.toString() ? `?${searchParams.toString()}` : ''
|
||||
|
||||
@@ -566,6 +566,6 @@ function sendAISearchResultEvent({
|
||||
ai_search_result_response_status: status,
|
||||
ai_search_result_connected_event_id: connectedEventId,
|
||||
eventGroupKey: ASK_AI_EVENT_GROUP,
|
||||
eventGroupId: eventGroupId,
|
||||
eventGroupId,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ export function SearchOverlay({
|
||||
if (searchParams.has('query')) {
|
||||
searchParams.delete('query')
|
||||
}
|
||||
router.push(`${selectedOption.url}?${searchParams.toString()}` || '')
|
||||
router.push(`${selectedOption.url}?${searchParams.toString()}`)
|
||||
onClose()
|
||||
}
|
||||
|
||||
@@ -412,12 +412,12 @@ export function SearchOverlay({
|
||||
if (searchParams.has('query')) {
|
||||
searchParams.delete('query')
|
||||
}
|
||||
window.open(`${url}?${searchParams.toString()}` || '', '_blank')
|
||||
window.open(`${url}?${searchParams.toString()}`, '_blank')
|
||||
}
|
||||
|
||||
// Handle keyboard navigation of suggestions
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||
let optionsLength = listElementsRef.current?.length ?? 0
|
||||
const optionsLength = listElementsRef.current?.length ?? 0
|
||||
if (event.key === 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
if (optionsLength > 0) {
|
||||
@@ -855,8 +855,8 @@ function renderSearchGroups(
|
||||
) {
|
||||
const groups = []
|
||||
|
||||
let isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
|
||||
let isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
|
||||
const isInAskAIState = askAIState?.isAskAIState && !askAIState.aiSearchError
|
||||
const isInAskAIStateButNoAnswer = isInAskAIState && askAIState.aiCouldNotAnswer
|
||||
|
||||
// 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
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function getAISearchAutocompleteResults({
|
||||
const t0 = new Date()
|
||||
const client = getElasticsearchClient() as Client
|
||||
|
||||
let searchQuery: any = {
|
||||
const searchQuery: any = {
|
||||
index: indexName,
|
||||
size,
|
||||
// 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
|
||||
// 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
|
||||
let force = {} as any
|
||||
const force = {} as any
|
||||
if (!req.query.query) {
|
||||
force.query = ''
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ export async function populateIndex(
|
||||
},
|
||||
)
|
||||
|
||||
if (bulkResponse.errors) {
|
||||
console.error(`Bulk response errors: ${bulkResponse.errors}`)
|
||||
if (bulkResponse.failed > 0) {
|
||||
console.error(`Bulk response failed: ${bulkResponse.failed} documents failed`)
|
||||
throw new Error('Bulk errors happened.')
|
||||
}
|
||||
const t1 = new Date()
|
||||
@@ -140,7 +140,7 @@ export async function updateAlias(
|
||||
)
|
||||
|
||||
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 } })
|
||||
console.log('Deleting old index', index.index)
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
import { sleep } from '@/search/lib/helpers/time'
|
||||
|
||||
export async function retryOnErrorTest(
|
||||
errorTest: (error: any) => boolean,
|
||||
callback: Function,
|
||||
export async function retryOnErrorTest<T>(
|
||||
errorTest: (error: unknown) => boolean,
|
||||
callback: () => Promise<T>,
|
||||
{
|
||||
attempts = 4,
|
||||
sleepTime = 1000,
|
||||
@@ -37,7 +37,7 @@ export async function retryOnErrorTest(
|
||||
jitterPercent?: number
|
||||
onError?: (error: Error, attempts: number, sleepTime: number) => void
|
||||
} = {},
|
||||
) {
|
||||
): Promise<T> {
|
||||
while (true) {
|
||||
try {
|
||||
return await callback()
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('AI Search Routes', () => {
|
||||
afterAll(() => stopMockServer())
|
||||
|
||||
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', {
|
||||
method: 'POST',
|
||||
@@ -80,7 +80,7 @@ describe('AI Search Routes', () => {
|
||||
})
|
||||
|
||||
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', {
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -95,7 +95,7 @@ describe('AI Search Routes', () => {
|
||||
})
|
||||
|
||||
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', {
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -110,7 +110,7 @@ describe('AI Search Routes', () => {
|
||||
})
|
||||
|
||||
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', {
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -140,7 +140,7 @@ describe('AI Search Routes', () => {
|
||||
if (response.body) {
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let chunks = []
|
||||
const chunks = []
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
|
||||
@@ -139,7 +139,7 @@ export type Context = {
|
||||
enPage?: Page
|
||||
productNames?: ProductNames
|
||||
currentVersion?: string
|
||||
process?: { env: {} }
|
||||
process?: { env: Record<string, string> }
|
||||
site?: {
|
||||
data: {
|
||||
ui: any
|
||||
|
||||
@@ -131,7 +131,8 @@ main().then(
|
||||
// Using any for token objects as liquidjs doesn't provide TypeScript types
|
||||
function removeInputProps(arrayOfObjects: any[]): any[] {
|
||||
return arrayOfObjects.map((obj: any) => {
|
||||
delete obj.input || delete obj.token.input
|
||||
delete obj.input
|
||||
delete obj.token.input
|
||||
return obj
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ interface WebhookActionData {
|
||||
codeExamples: Array<CodeSample>
|
||||
availability: Array<string>
|
||||
action: string
|
||||
payloadExample?: Object
|
||||
payloadExample?: object
|
||||
}
|
||||
export interface WebhookAction {
|
||||
name: string
|
||||
|
||||
@@ -96,7 +96,9 @@ export const getServerSideProps: GetServerSideProps<Props> = async (context) =>
|
||||
webhooks.map((webhook) => webhook.data.category),
|
||||
context,
|
||||
)
|
||||
webhooksMiniTocs && miniTocItems.push(...webhooksMiniTocs)
|
||||
if (webhooksMiniTocs) {
|
||||
miniTocItems.push(...webhooksMiniTocs)
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import * as github from '@actions/github'
|
||||
import github from '@actions/github'
|
||||
import core from '@actions/core'
|
||||
|
||||
import walk from 'walk-sync'
|
||||
|
||||
@@ -22,7 +22,7 @@ async function main() {
|
||||
console.log('ID:', issue.id)
|
||||
console.log('Number:', issue.number)
|
||||
console.log('URL:', issue.html_url)
|
||||
number = issue.number
|
||||
number = String(issue.number)
|
||||
if (number) {
|
||||
// We've found the issue (pull request), but before we accept
|
||||
// this `number`, check that the issue isn't locked.
|
||||
|
||||
@@ -234,12 +234,12 @@ async function searchCode(
|
||||
}
|
||||
}
|
||||
|
||||
async function secondaryRateLimitRetry(
|
||||
callable: Function,
|
||||
args: Record<string, any>,
|
||||
async function secondaryRateLimitRetry<T, TArgs = Record<string, unknown>>(
|
||||
callable: (args: TArgs) => Promise<T>,
|
||||
args: TArgs,
|
||||
maxAttempts = 10,
|
||||
sleepTime = 1000,
|
||||
) {
|
||||
): Promise<T> {
|
||||
try {
|
||||
const response = await callable(args)
|
||||
return response
|
||||
|
||||
@@ -7,10 +7,10 @@ const DEFAULT_ATTEMPTS = parseInt(process.env.SECONDARY_RATELIMIT_RETRY_ATTEMPTS
|
||||
// "You have exceeded a secondary rate limit".
|
||||
// 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
|
||||
export async function octoSecondaryRatelimitRetry(
|
||||
fn: Function,
|
||||
export async function octoSecondaryRatelimitRetry<T>(
|
||||
fn: () => Promise<T>,
|
||||
{ attempts = DEFAULT_ATTEMPTS, sleepTime = DEFAULT_SLEEPTIME } = {},
|
||||
) {
|
||||
): Promise<T> {
|
||||
let tries = 0
|
||||
while (true) {
|
||||
try {
|
||||
|
||||
@@ -25,11 +25,17 @@ export function readFiles(dir = 'content', ext = 'md', opts = {}) {
|
||||
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))
|
||||
}
|
||||
|
||||
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)])
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user