From dff55b2c9ce4237a016071d51601cbb09eb58d88 Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 20 Feb 2026 15:53:30 -0800 Subject: [PATCH 1/8] Refactor createOrUpdateStagingDeploy for clarity and maintainability Extracts StagingDeploy-specific logic from GithubUtils into a dedicated StagingDeployUtils module, restructures the action as a standalone ts-node CLI script with a thin GitHub Actions wrapper, and applies several code quality improvements: - Break run() into focused helpers (buildNewChecklistParams, buildUpdateChecklistParams) - Consolidate checkbox state preservation into preserveCheckboxState() - Generalize checklist parsers into a single parseChecklistSection() helper - Accept PR numbers directly instead of URL roundtripping - Use dedent template literals for issue body construction - Remove void from generateStagingDeployCashBodyAndAssignees return type - Convert .then() chains to async/await throughout Co-authored-by: Cursor --- .../createOrUpdateStagingDeploy.ts | 335 +---------- .../isStagingDeployLocked.ts | 4 +- .github/libs/GithubUtils.ts | 319 +---------- .github/libs/StagingDeployUtils.ts | 274 +++++++++ scripts/createOrUpdateStagingDeploy.ts | 365 ++++++++++++ tests/unit/GithubUtilsTest.ts | 536 +----------------- tests/unit/StagingDeployUtilsTest.ts | 516 +++++++++++++++++ tests/unit/createOrUpdateStagingDeployTest.ts | 71 +-- tests/unit/isStagingDeployLockedTest.ts | 16 +- 9 files changed, 1212 insertions(+), 1224 deletions(-) create mode 100644 .github/libs/StagingDeployUtils.ts create mode 100644 scripts/createOrUpdateStagingDeploy.ts create mode 100644 tests/unit/StagingDeployUtilsTest.ts diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts index 63fc66355b89d..fa6bbff44858d 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts @@ -1,333 +1,22 @@ import * as core from '@actions/core'; -import {format} from 'date-fns/format'; -import fs from 'fs'; -import CONST from '@github/libs/CONST'; -import GithubUtils from '@github/libs/GithubUtils'; -import type {StagingDeployCashData} from '@github/libs/GithubUtils'; -import GitUtils from '@github/libs/GitUtils'; -import type {MergedPR, SubmoduleUpdate} from '@github/libs/GitUtils'; - -type IssuesCreateResponse = Awaited>['data']; - -type PackageJson = { - version: string; -}; - -type TimelineEntry = {type: 'pr'; prNumber: number; date: string} | {type: 'submodule'; version: string; date: string; commit: string}; - -async function buildChronologicalSection({ - chronologicalPREntries, - submoduleUpdates, - mergedMobileExpensifyPREntries, -}: { - chronologicalPREntries: MergedPR[]; - submoduleUpdates: SubmoduleUpdate[]; - mergedMobileExpensifyPREntries: MergedPR[]; -}): Promise { - if (chronologicalPREntries.length === 0 && submoduleUpdates.length === 0 && mergedMobileExpensifyPREntries.length === 0) { - return ''; - } - - // Look up workflow run URLs for each submodule update commit - const submoduleRunURLs = new Map(); - const results = await Promise.allSettled( - submoduleUpdates.map(async (update) => { - const runURL = await GithubUtils.getWorkflowRunURLForCommit(update.commit, 'testBuildOnPush.yml'); - return {commit: update.commit, runURL}; - }), - ); - for (const result of results) { - if (result.status === 'fulfilled') { - submoduleRunURLs.set(result.value.commit, result.value.runURL); - } - } - - // Group Mobile-Expensify PRs by the submodule update that introduced them. - // A Mobile-Expensify PR is assigned to the first submodule bump whose date >= PR merge date, - // because merging to Mobile-Expensify doesn't matter until the submodule is actually bumped in App. - const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); - const mobileExpensifyPRsBySubmodule = new Map(); - const mobileExpensifyPRsPendingSubmoduleUpdate: MergedPR[] = []; - for (const mobileExpensifyPR of mergedMobileExpensifyPREntries) { - const matchingUpdate = sortedSubmoduleUpdates.find((update) => update.date.localeCompare(mobileExpensifyPR.date) >= 0); - if (matchingUpdate) { - const existing = mobileExpensifyPRsBySubmodule.get(matchingUpdate.commit) ?? []; - existing.push(mobileExpensifyPR); - mobileExpensifyPRsBySubmodule.set(matchingUpdate.commit, existing); - } else { - mobileExpensifyPRsPendingSubmoduleUpdate.push(mobileExpensifyPR); - } - } - - // Merge PRs and submodule updates into a single chronological timeline - const timeline: TimelineEntry[] = [ - ...chronologicalPREntries.map((pr): TimelineEntry => ({type: 'pr', prNumber: pr.prNumber, date: pr.date})), - ...submoduleUpdates.map((update): TimelineEntry => ({type: 'submodule', version: update.version, date: update.date, commit: update.commit})), - ].sort((a, b) => a.date.localeCompare(b.date)); - - let section = '
\r\nChronologically ordered merged PRs (oldest first)\r\n\r\n'; - let prIndex = 0; - for (const entry of timeline) { - if (entry.type === 'submodule') { - prIndex++; - const runURL = submoduleRunURLs.get(entry.commit); - const buildLink = runURL ? ` — [Adhoc Build](${runURL})` : ` — ${entry.commit.substring(0, 7)}`; - section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\r\n`; - const mobileExpensifyPRs = mobileExpensifyPRsBySubmodule.get(entry.commit); - if (mobileExpensifyPRs) { - const sortedMobileExpensifyPRs = [...mobileExpensifyPRs].sort((a, b) => a.date.localeCompare(b.date)); - for (const mobileExpensifyPR of sortedMobileExpensifyPRs) { - const mobileExpensifyUrl = GithubUtils.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST.MOBILE_EXPENSIFY_URL); - section += ` ↳ ${mobileExpensifyUrl}\r\n`; - } - } - } else { - prIndex++; - const url = GithubUtils.getPullRequestURLFromNumber(entry.prNumber, CONST.APP_REPO_URL); - section += `${prIndex}. ${url}\r\n`; - } - } - if (mobileExpensifyPRsPendingSubmoduleUpdate.length > 0) { - const sortedPending = [...mobileExpensifyPRsPendingSubmoduleUpdate].sort((a, b) => a.date.localeCompare(b.date)); - section += `\r\n--- PRs waiting for Mobile-Expensify submodule update\r\n`; - for (const mobileExpensifyPR of sortedPending) { - const mobileExpensifyUrl = GithubUtils.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST.MOBILE_EXPENSIFY_URL); - section += `${mobileExpensifyUrl}\r\n`; - } - } - section += '\r\n
'; - return section; -} - -async function run(): Promise { - // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time - const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')) as PackageJson; - // The checklist will use the package.json version, e.g. '1.2.3-4' - const newVersion = packageJson.version; - // The staging tag will use the package.json version with a '-staging' suffix, e.g. '1.2.3-4-staging' - const newStagingTag = `${packageJson.version}-staging`; +import run from '@scripts/createOrUpdateStagingDeploy'; +async function main() { try { - // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers - const {data: recentDeployChecklists} = await GithubUtils.octokit.issues.listForRepo({ - log: console, - owner: CONST.GITHUB_OWNER, - repo: CONST.APP_REPO, - labels: CONST.LABELS.STAGING_DEPLOY, - state: 'all', - }); - - // Look at the state of the most recent StagingDeployCash, - // if it is open then we'll update the existing one, otherwise, we'll create a new one. - const mostRecentChecklist = recentDeployChecklists.at(0); - - if (!mostRecentChecklist) { - throw new Error('Could not find the most recent checklist'); - } - - const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; - const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); - if (shouldCreateNewDeployChecklist) { - console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); - } else { - console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); - } - - if (!previousChecklist) { - throw new Error('Could not find the previous checklist'); - } - - // Parse the data from the previous and current checklists into the format used to generate the checklist - const previousChecklistData = GithubUtils.getStagingDeployCashData(previousChecklist); - const currentChecklistData: StagingDeployCashData | undefined = shouldCreateNewDeployChecklist ? undefined : GithubUtils.getStagingDeployCashData(mostRecentChecklist); - - // Find the list of PRs merged between the current checklist and the previous checklist - const {mergedPRs: mergedPREntries, submoduleUpdates} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.APP_REPO); - const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); - - // mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out - const previousPRNumbers = new Set(previousChecklistData.PRList.map((pr) => pr.number)); - const previousMobileExpensifyPRNumbers = new Set(previousChecklistData.PRListMobileExpensify.map((pr) => pr.number)); - core.startGroup('Filtering PRs:'); - core.info('mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out'); - core.info(`Found ${previousPRNumbers.size} PRs in the previous checklist:`); - core.info(JSON.stringify(Array.from(previousPRNumbers))); - const newPRNumbers = mergedPRs.filter((prNum) => !previousPRNumbers.has(prNum)); - core.info(`Found ${newPRNumbers.length} PRs deployed since the previous checklist:`); - core.info(JSON.stringify(newPRNumbers)); - - // Filter out cherry-picked PRs that were released with the previous checklist - const removedPRs = mergedPRs.filter((prNum) => previousPRNumbers.has(prNum)); - if (removedPRs.length > 0) { - core.info(`⚠️⚠️ Filtered out the following cherry-picked PRs that were released with the previous checklist: ${removedPRs.join(', ')} ⚠️⚠️`); - } - core.endGroup(); - console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); - - // Get merged Mobile-Expensify PRs (with dates for chronological grouping by submodule update) - let mergedMobileExpensifyPREntries: MergedPR[] = []; - try { - const {mergedPRs: allMobileExpensifyPREntries} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.MOBILE_EXPENSIFY_REPO); - mergedMobileExpensifyPREntries = allMobileExpensifyPREntries.filter((pr) => !previousMobileExpensifyPRNumbers.has(pr.prNumber)); - - const allCount = allMobileExpensifyPREntries.length; - const newCount = mergedMobileExpensifyPREntries.length; - console.info(`Found ${allCount} total Mobile-Expensify PRs, ${newCount} new ones after filtering:`); - console.info(`Mobile-Expensify PRs: ${mergedMobileExpensifyPREntries.map((pr) => pr.prNumber).join(', ')}`); - - // Log the Mobile-Expensify PRs that were filtered out - const removedMobileExpensifyPRs = allMobileExpensifyPREntries.filter((pr) => previousMobileExpensifyPRNumbers.has(pr.prNumber)); - if (removedMobileExpensifyPRs.length > 0) { - core.info( - `⚠️⚠️ Filtered out the following cherry-picked Mobile-Expensify PRs that were released with the previous checklist: ${removedMobileExpensifyPRs.map((pr) => pr.prNumber).join(', ')} ⚠️⚠️`, - ); - } - } catch (error) { - // Check if this is a forked repository - if (process.env.GITHUB_REPOSITORY !== `${CONST.GITHUB_OWNER}/${CONST.APP_REPO}`) { - console.warn( - "⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessble. This is expected for development/testing on forks.", - ); - } else { - console.error('Failed to fetch Mobile-Expensify PRs:', error); - } - } - - const chronologicalPREntries = mergedPREntries.filter((pr) => !previousPRNumbers.has(pr.prNumber)).sort((a, b) => a.date.localeCompare(b.date)); - const chronologicalSection = await buildChronologicalSection({ - chronologicalPREntries, - submoduleUpdates, - mergedMobileExpensifyPREntries, - }); - - // Next, we generate the checklist body - let checklistBody = ''; - let checklistAssignees: string[] = []; - if (shouldCreateNewDeployChecklist) { - const stagingDeployCashBodyAndAssignees = await GithubUtils.generateStagingDeployCashBodyAndAssignees({ - tag: newVersion, - PRList: newPRNumbers.map((value) => GithubUtils.getPullRequestURLFromNumber(value, CONST.APP_REPO_URL)), - PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => GithubUtils.getPullRequestURLFromNumber(pr.prNumber, CONST.MOBILE_EXPENSIFY_URL)), - previousTag: previousChecklistData.version, - chronologicalSection, - }); - if (stagingDeployCashBodyAndAssignees) { - checklistBody = stagingDeployCashBodyAndAssignees.issueBody; - checklistAssignees = stagingDeployCashBodyAndAssignees.issueAssignees.filter(Boolean) as string[]; - } - } else { - // Generate the updated PR list, preserving the previous state of `isVerified` for existing PRs - const PRList = newPRNumbers.map((prNum) => { - const indexOfPRInCurrentChecklist = currentChecklistData?.PRList.findIndex((pr) => pr.number === prNum) ?? -1; - const isVerified = indexOfPRInCurrentChecklist >= 0 ? currentChecklistData?.PRList[indexOfPRInCurrentChecklist].isVerified : false; - return { - number: prNum, - url: GithubUtils.getPullRequestURLFromNumber(prNum, CONST.APP_REPO_URL), - isVerified, - }; - }); - - // Generate the updated Mobile-Expensify PR list, preserving the previous state of `isVerified` for existing PRs - const PRListMobileExpensify = mergedMobileExpensifyPREntries.map((entry) => { - const indexOfPRInCurrentChecklist = currentChecklistData?.PRListMobileExpensify.findIndex((pr) => pr.number === entry.prNumber) ?? -1; - const isVerified = indexOfPRInCurrentChecklist >= 0 ? currentChecklistData?.PRListMobileExpensify[indexOfPRInCurrentChecklist].isVerified : false; - return { - number: entry.prNumber, - url: GithubUtils.getPullRequestURLFromNumber(entry.prNumber, CONST.MOBILE_EXPENSIFY_URL), - isVerified, - }; - }); - - // Generate the deploy blocker list, preserving the previous state of `isResolved` - const openDeployBlockers = await GithubUtils.paginate(GithubUtils.octokit.issues.listForRepo, { - log: console, - owner: CONST.GITHUB_OWNER, - repo: CONST.APP_REPO, - labels: CONST.LABELS.DEPLOY_BLOCKER, - }); - - // First, make sure we include all current deploy blockers - const deployBlockers = openDeployBlockers.map((deployBlocker) => { - const indexInCurrentChecklist = currentChecklistData?.deployBlockers.findIndex((item) => item.number === deployBlocker.number) ?? -1; - const isResolved = indexInCurrentChecklist >= 0 ? currentChecklistData?.deployBlockers[indexInCurrentChecklist].isResolved : false; - return { - number: deployBlocker.number, - url: deployBlocker.html_url, - isResolved, - }; - }); - - // Then make sure we include any demoted or closed blockers as well, and just check them off automatically - for (const deployBlocker of currentChecklistData?.deployBlockers ?? []) { - const isResolved = deployBlockers.findIndex((openBlocker) => openBlocker.number === deployBlocker.number) < 0; - deployBlockers.push({ - ...deployBlocker, - isResolved, - }); - } - - // Include any existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list - for (const existingPR of currentChecklistData?.PRListMobileExpensify ?? []) { - const isAlreadyIncluded = PRListMobileExpensify.findIndex((pr) => pr.number === existingPR.number) >= 0; - if (!isAlreadyIncluded) { - PRListMobileExpensify.push(existingPR); - } - } - - const didVersionChange = newVersion !== currentChecklistData?.version; - const stagingDeployCashBodyAndAssignees = await GithubUtils.generateStagingDeployCashBodyAndAssignees({ - tag: newVersion, - PRList: PRList.map((pr) => pr.url), - PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.url), - verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.url), - verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.url), - deployBlockers: deployBlockers.map((blocker) => blocker.url), - resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), - resolvedInternalQAPRs: currentChecklistData?.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.url), - isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, - isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, - previousTag: previousChecklistData.version, - chronologicalSection, - }); - if (stagingDeployCashBodyAndAssignees) { - checklistBody = stagingDeployCashBodyAndAssignees.issueBody; - checklistAssignees = stagingDeployCashBodyAndAssignees.issueAssignees.filter(Boolean) as string[]; - } - } - - // Finally, create or update the checklist - const defaultPayload = { - owner: CONST.GITHUB_OWNER, - repo: CONST.APP_REPO, - body: checklistBody, - }; - - if (shouldCreateNewDeployChecklist) { - const {data: newChecklist} = await GithubUtils.octokit.issues.create({ - ...defaultPayload, - title: `Deploy Checklist: New Expensify ${format(new Date(), CONST.DATE_FORMAT_STRING)}`, - labels: [CONST.LABELS.STAGING_DEPLOY, CONST.LABELS.LOCK_DEPLOY], - assignees: [CONST.APPLAUSE_BOT as string].concat(checklistAssignees), - }); - console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); - return newChecklist; - } - - const {data: updatedChecklist} = await GithubUtils.octokit.issues.update({ - ...defaultPayload, - // eslint-disable-next-line @typescript-eslint/naming-convention - issue_number: currentChecklistData?.number ?? 0, - }); - console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); - return updatedChecklist; - } catch (err: unknown) { - console.error('An unknown error occurred!', err); - core.setFailed(err as Error); + const token = core.getInput('GITHUB_TOKEN', {required: true}); + process.env.GITHUB_TOKEN = token; + await run(); + } catch (e) { + if (e instanceof Error) { + core.setFailed(e); + return; + } + core.setFailed('An unknown error occurred.'); } } if (require.main === module) { - run(); + main(); } export default run; diff --git a/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts b/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts index ce19ce62f8af8..d8cdfa2675db0 100644 --- a/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts +++ b/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts @@ -1,8 +1,8 @@ import * as core from '@actions/core'; -import GithubUtils from '@github/libs/GithubUtils'; +import {getStagingDeployCash} from '@github/libs/StagingDeployUtils'; const run = function (): Promise { - return GithubUtils.getStagingDeployCash() + return getStagingDeployCash() .then(({labels, number}) => { const labelsNames = labels.map((label) => { if (typeof label === 'string') { diff --git a/.github/libs/GithubUtils.ts b/.github/libs/GithubUtils.ts index 979080a6b4ccd..b11afb3ec72cf 100644 --- a/.github/libs/GithubUtils.ts +++ b/.github/libs/GithubUtils.ts @@ -11,14 +11,10 @@ import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/di import type {Api} from '@octokit/plugin-rest-endpoint-methods/dist-types/types'; import {throttling} from '@octokit/plugin-throttling'; import {RequestError} from '@octokit/request-error'; -import arrayDifference from './arrayDifference'; import CONST from './CONST'; -import {isEmptyObject} from './isEmptyObject'; type OctokitOptions = {method: string; url: string; request: {retryCount: number}}; -type ListForRepoResult = RestEndpointMethodTypes['issues']['listForRepo']['response']; - type OctokitIssueItem = OctokitComponents['schemas']['issue']; type ListForRepoMethod = RestEndpointMethods['issues']['listForRepo']; @@ -32,38 +28,6 @@ type CommitType = { date: string; }; -type StagingDeployCashPR = { - url: string; - number: number; - isVerified: boolean; -}; - -type StagingDeployCashBlocker = { - url: string; - number: number; - isResolved: boolean; -}; - -type StagingDeployCashBody = { - issueBody: string; - issueAssignees: Array; -}; - -type StagingDeployCashParams = { - tag: string; - PRList: string[]; - PRListMobileExpensify?: string[]; - verifiedPRList?: string[]; - verifiedPRListMobileExpensify?: string[]; - deployBlockers?: string[]; - resolvedDeployBlockers?: string[]; - resolvedInternalQAPRs?: string[]; - isSentryChecked?: boolean; - isGHStatusChecked?: boolean; - previousTag?: string; - chronologicalSection?: string; -}; - type OctokitArtifact = OctokitComponents['schemas']['artifact']; type OctokitPR = OctokitComponents['schemas']['pull-request-simple']; @@ -72,21 +36,6 @@ type CreateCommentResponse = RestEndpointMethodTypes['issues']['createComment'][ type ListCommentsResponse = RestEndpointMethodTypes['issues']['listComments']['response']; -type StagingDeployCashData = { - title: string; - url: string; - number: number; - labels: OctokitIssueItem['labels']; - PRList: StagingDeployCashPR[]; - PRListMobileExpensify: StagingDeployCashPR[]; - deployBlockers: StagingDeployCashBlocker[]; - internalQAPRList: StagingDeployCashBlocker[]; - isSentryChecked: boolean; - isGHStatusChecked: boolean; - version: string; - tag: string; -}; - type InternalOctokit = OctokitCore & Api & {paginate: PaginateInterface}; class GithubUtils { @@ -181,272 +130,6 @@ class GithubUtils { return (this.internalOctokit as InternalOctokit).paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash(): Promise { - return this.octokit.issues - .listForRepo({ - owner: CONST.GITHUB_OWNER, - repo: CONST.APP_REPO, - labels: CONST.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({data}: ListForRepoResult) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST.LABELS.STAGING_DEPLOY} issue.`); - } - - if (data.length > 1) { - throw new Error(`Found more than one ${CONST.LABELS.STAGING_DEPLOY} issue.`); - } - - const issue = data.at(0); - - if (!issue) { - throw new Error(`Found an undefined ${CONST.LABELS.STAGING_DEPLOY} issue.`); - } - - return this.getStagingDeployCashData(issue); - }); - } - - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue: OctokitIssueItem): StagingDeployCashData { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } catch (exception) { - throw new Error(`Unable to find ${CONST.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue: OctokitIssueItem): StagingDeployCashPR[] { - let PRListSection: RegExpMatchArray | string | null = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - - return PRList.sort((a, b) => a.number - b.number); - } - - static getStagingDeployCashPRListMobileExpensify(issue: OctokitIssueItem): StagingDeployCashPR[] { - let mobileExpensifySection: RegExpMatchArray | string | null = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue: OctokitIssueItem): StagingDeployCashBlocker[] { - let deployBlockerSection: RegExpMatchArray | string | null = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - - return deployBlockers.sort((a, b) => a.number - b.number); - } - - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue: OctokitIssueItem): StagingDeployCashBlocker[] { - let internalQASection: RegExpMatchArray | string | null = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - - return internalQAPRs.sort((a, b) => a.number - b.number); - } - - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ - tag, - PRList, - PRListMobileExpensify = [], - verifiedPRList = [], - verifiedPRListMobileExpensify = [], - deployBlockers = [], - resolvedDeployBlockers = [], - resolvedInternalQAPRs = [], - isSentryChecked = false, - isGHStatusChecked = false, - previousTag = '', - chronologicalSection = '', - }: StagingDeployCashParams): Promise { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !isEmptyObject(pr.labels.find((item) => item.name === CONST.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({url: pr.html_url, mergerLogin})))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce>((acc, {url, mergerLogin}) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - - const sortedPRList = [...new Set(arrayDifference(PRList, Object.keys(internalQAPRMap)))].sort( - (a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b), - ); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort( - (a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b), - ); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort( - (a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b), - ); - - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - - issueBody += '\r\n'; - - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - - // Internal QA PR list - if (!isEmptyObject(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${ - isSentryChecked ? 'x' : ' ' - }] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${ - isSentryChecked ? 'x' : ' ' - }] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = {issueBody, issueAssignees}; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } - /** * Fetch all pull requests given a list of PR numbers. */ @@ -820,4 +503,4 @@ class GithubUtils { } export default GithubUtils; -export type {ListForRepoMethod, InternalOctokit, CreateCommentResponse, ListCommentsResponse, StagingDeployCashData, CommitType}; +export type {OctokitIssueItem, ListForRepoMethod, InternalOctokit, CreateCommentResponse, ListCommentsResponse, CommitType}; diff --git a/.github/libs/StagingDeployUtils.ts b/.github/libs/StagingDeployUtils.ts new file mode 100644 index 0000000000000..1e34ce971e3af --- /dev/null +++ b/.github/libs/StagingDeployUtils.ts @@ -0,0 +1,274 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import type {components as OctokitComponents} from '@octokit/openapi-types/types'; +import dedent from '@libs/StringUtils/dedent'; +import arrayDifference from './arrayDifference'; +import CONST from './CONST'; +import GithubUtils from './GithubUtils'; +import {isEmptyObject} from './isEmptyObject'; + +type OctokitIssueItem = OctokitComponents['schemas']['issue']; + +type StagingDeployCashPR = { + url: string; + number: number; + isVerified: boolean; +}; + +type StagingDeployCashBlocker = { + url: string; + number: number; + isResolved: boolean; +}; + +type StagingDeployCashBody = { + issueBody: string; + issueAssignees: string[]; +}; + +type StagingDeployCashParams = { + tag: string; + PRList: number[]; + PRListMobileExpensify?: number[]; + verifiedPRList?: number[]; + verifiedPRListMobileExpensify?: number[]; + deployBlockers?: string[]; + resolvedDeployBlockers?: string[]; + resolvedInternalQAPRs?: number[]; + isSentryChecked?: boolean; + isGHStatusChecked?: boolean; + previousTag?: string; + chronologicalSection?: string; +}; + +type StagingDeployCashData = { + title: string; + url: string; + number: number; + labels: OctokitIssueItem['labels']; + PRList: StagingDeployCashPR[]; + PRListMobileExpensify: StagingDeployCashPR[]; + deployBlockers: StagingDeployCashBlocker[]; + internalQAPRList: StagingDeployCashBlocker[]; + isSentryChecked: boolean; + isGHStatusChecked: boolean; + version: string; + tag: string; +}; + +/** + * Generic checklist section parser. Extracts a section from the issue body, + * parses checkbox items within it, and returns typed objects sorted by number. + */ +function parseChecklistSection( + issueBody: string | null | undefined, + sectionRegex: RegExp, + itemRegex: RegExp, + checkedKey: K, + urlTransform?: (url: string) => string, +): Array<{url: string; number: number} & Record> { + const sectionMatch = issueBody?.match(sectionRegex) ?? null; + if (sectionMatch?.length !== 2) { + return []; + } + const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { + const rawUrl = match[2]; + return { + url: urlTransform ? urlTransform(rawUrl) : rawUrl, + number: Number.parseInt(match[3], 10), + [checkedKey]: match[1] === 'x', + } as {url: string; number: number} & Record; + }); + return items.sort((a, b) => a.number - b.number); +} + +function getStagingDeployCashPRList(issue: OctokitIssueItem): StagingDeployCashPR[] { + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); + if (result.length === 0) { + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + } + return result; +} + +function getStagingDeployCashPRListMobileExpensify(issue: OctokitIssueItem): StagingDeployCashPR[] { + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); +} + +function getStagingDeployCashDeployBlockers(issue: OctokitIssueItem): StagingDeployCashBlocker[] { + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); +} + +function getStagingDeployCashInternalQA(issue: OctokitIssueItem): StagingDeployCashBlocker[] { + return parseChecklistSection( + issue.body, + /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, + new RegExp(`- \\[([ x])]\\s(${CONST.PULL_REQUEST_REGEX.source})`, 'g'), + 'isResolved', + (url) => url.split('-').at(0)?.trim() ?? '', + ); +} + +async function getStagingDeployCash(): Promise { + const {data} = await GithubUtils.octokit.issues.listForRepo({ + owner: CONST.GITHUB_OWNER, + repo: CONST.APP_REPO, + labels: CONST.LABELS.STAGING_DEPLOY, + state: 'open', + }); + + if (!data.length) { + throw new Error(`Unable to find ${CONST.LABELS.STAGING_DEPLOY} issue.`); + } + + if (data.length > 1) { + throw new Error(`Found more than one ${CONST.LABELS.STAGING_DEPLOY} issue.`); + } + + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST.LABELS.STAGING_DEPLOY} issue.`); + } + + return getStagingDeployCashData(issue); +} + +function getStagingDeployCashData(issue: OctokitIssueItem): StagingDeployCashData { + try { + const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); + const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); + + return { + title: issue.title, + url: issue.url, + number: GithubUtils.getIssueOrPullRequestNumberFromURL(issue.url), + labels: issue.labels, + PRList: getStagingDeployCashPRList(issue), + PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), + deployBlockers: getStagingDeployCashDeployBlockers(issue), + internalQAPRList: getStagingDeployCashInternalQA(issue), + isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, + isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, + version, + tag: `${version}-staging`, + }; + } catch (exception) { + throw new Error(`Unable to find ${CONST.LABELS.STAGING_DEPLOY} issue with correct data.`); + } +} + +/** + * Generate the issue body and assignees for a StagingDeployCash checklist. + * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. + */ +async function generateStagingDeployCashBodyAndAssignees({ + tag, + PRList, + PRListMobileExpensify = [], + verifiedPRList = [], + verifiedPRListMobileExpensify = [], + deployBlockers = [], + resolvedDeployBlockers = [], + resolvedInternalQAPRs = [], + isSentryChecked = false, + isGHStatusChecked = false, + previousTag = '', + chronologicalSection = '', +}: StagingDeployCashParams): Promise { + const data = await GithubUtils.fetchAllPullRequests(PRList); + + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !isEmptyObject(pr.labels.find((item) => item.name === CONST.LABELS.INTERNAL_QA))) : []; + const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({number: pr.number, mergerLogin})))); + + const internalQAPRMap = new Map(); + for (const {number, mergerLogin} of mergerResults) { + internalQAPRMap.set(number, mergerLogin); + } + console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); + + const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; + console.log('Found the following NO QA PRs:', noQAPRNumbers); + + const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); + const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); + const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); + const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); + + const internalQAPRNumbers = new Set(internalQAPRMap.keys()); + const sortedPRList = [...new Set(arrayDifference(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); + const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); + + const sections: string[] = []; + + // Header + let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; + if (sortedPRListMobileExpensify.length > 0) { + header += `**Mobile-Expensify Changes:** https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; + } + sections.push(header); + + // PR list + if (sortedPRList.length > 0) { + const items = sortedPRList + .map((prNumber) => { + const url = GithubUtils.getPullRequestURLFromNumber(prNumber, CONST.APP_REPO_URL); + return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); + } + + // Mobile-Expensify PR list + if (sortedPRListMobileExpensify.length > 0) { + const items = sortedPRListMobileExpensify + .map((prNumber) => { + const url = GithubUtils.getPullRequestURLFromNumber(prNumber, CONST.MOBILE_EXPENSIFY_URL); + return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); + } + + // Internal QA PR list + if (internalQAPRMap.size > 0) { + console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); + const items = [...internalQAPRMap.entries()] + .map(([prNumber, merger]) => { + const url = GithubUtils.getPullRequestURLFromNumber(prNumber, CONST.APP_REPO_URL); + return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; + }) + .join('\n'); + sections.push(`**Internal QA:**\n${items}\n`); + } + + // Deploy blockers + if (deployBlockers.length > 0) { + const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); + sections.push(`**Deploy Blockers:**\n${items}\n`); + } + + // Chronological section + if (chronologicalSection) { + sections.push(chronologicalSection); + } + + // Deployer verifications + const check = (checked: boolean) => (checked ? 'x' : ' '); + sections.push( + dedent(` + **Deployer verifications:** + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. + `).trimEnd(), + ); + + // Footer + sections.push('cc @Expensify/applauseleads\n'); + + const issueBody = sections.join('\n'); + const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login): login is string => login !== undefined); + return {issueBody, issueAssignees}; +} + +export {getStagingDeployCash, getStagingDeployCashData, generateStagingDeployCashBodyAndAssignees, parseChecklistSection}; +export type {StagingDeployCashPR, StagingDeployCashBlocker, StagingDeployCashBody, StagingDeployCashParams, StagingDeployCashData}; diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateStagingDeploy.ts new file mode 100644 index 0000000000000..8c51fe0ddc34f --- /dev/null +++ b/scripts/createOrUpdateStagingDeploy.ts @@ -0,0 +1,365 @@ +#!/usr/bin/env ts-node +/* eslint-disable @typescript-eslint/naming-convention */ +import * as core from '@actions/core'; +import {format} from 'date-fns/format'; +import fs from 'fs'; +import CONST from '@github/libs/CONST'; +import GithubUtils from '@github/libs/GithubUtils'; +import GitUtils from '@github/libs/GitUtils'; +import type {MergedPR, SubmoduleUpdate} from '@github/libs/GitUtils'; +import {generateStagingDeployCashBodyAndAssignees, getStagingDeployCashData} from '@github/libs/StagingDeployUtils'; +import type {StagingDeployCashBlocker, StagingDeployCashData, StagingDeployCashPR} from '@github/libs/StagingDeployUtils'; + +type IssuesCreateResponse = Awaited>['data']; + +type PackageJson = { + version: string; +}; + +type TimelineEntry = {type: 'pr'; prNumber: number; date: string} | {type: 'submodule'; version: string; date: string; commit: string}; + +/** + * Preserve the checked state (isVerified/isResolved) from a previous checklist when building a new one. + */ +function preserveCheckboxState( + items: Array<{number: number; url: string}>, + currentList: T[] | undefined, + checkedKey: 'isVerified' | 'isResolved', +): T[] { + return items.map((item) => { + const existing = currentList?.find((c) => c.number === item.number); + return {...item, [checkedKey]: existing ? (existing as Record)[checkedKey] : false} as T; + }); +} + +async function buildChronologicalSection({ + chronologicalPREntries, + submoduleUpdates, + mergedMobileExpensifyPREntries, +}: { + chronologicalPREntries: MergedPR[]; + submoduleUpdates: SubmoduleUpdate[]; + mergedMobileExpensifyPREntries: MergedPR[]; +}): Promise { + if (chronologicalPREntries.length === 0 && submoduleUpdates.length === 0 && mergedMobileExpensifyPREntries.length === 0) { + return ''; + } + + const submoduleRunURLs = new Map(); + const results = await Promise.allSettled( + submoduleUpdates.map(async (update) => { + const runURL = await GithubUtils.getWorkflowRunURLForCommit(update.commit, 'testBuildOnPush.yml'); + return {commit: update.commit, runURL}; + }), + ); + for (const result of results) { + if (result.status === 'fulfilled') { + submoduleRunURLs.set(result.value.commit, result.value.runURL); + } + } + + const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); + const mobileExpensifyPRsBySubmodule = new Map(); + const mobileExpensifyPRsPendingSubmoduleUpdate: MergedPR[] = []; + for (const mobileExpensifyPR of mergedMobileExpensifyPREntries) { + const matchingUpdate = sortedSubmoduleUpdates.find((update) => update.date.localeCompare(mobileExpensifyPR.date) >= 0); + if (matchingUpdate) { + const existing = mobileExpensifyPRsBySubmodule.get(matchingUpdate.commit) ?? []; + existing.push(mobileExpensifyPR); + mobileExpensifyPRsBySubmodule.set(matchingUpdate.commit, existing); + } else { + mobileExpensifyPRsPendingSubmoduleUpdate.push(mobileExpensifyPR); + } + } + + const timeline: TimelineEntry[] = [ + ...chronologicalPREntries.map((pr): TimelineEntry => ({type: 'pr', prNumber: pr.prNumber, date: pr.date})), + ...submoduleUpdates.map((update): TimelineEntry => ({type: 'submodule', version: update.version, date: update.date, commit: update.commit})), + ].sort((a, b) => a.date.localeCompare(b.date)); + + let section = '
\nChronologically ordered merged PRs (oldest first)\n\n'; + let prIndex = 0; + for (const entry of timeline) { + if (entry.type === 'submodule') { + prIndex++; + const runURL = submoduleRunURLs.get(entry.commit); + const buildLink = runURL ? ` — [Adhoc Build](${runURL})` : ` — ${entry.commit.substring(0, 7)}`; + section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\n`; + const mobileExpensifyPRs = mobileExpensifyPRsBySubmodule.get(entry.commit); + if (mobileExpensifyPRs) { + const sortedMobileExpensifyPRs = [...mobileExpensifyPRs].sort((a, b) => a.date.localeCompare(b.date)); + for (const mobileExpensifyPR of sortedMobileExpensifyPRs) { + const mobileExpensifyUrl = GithubUtils.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST.MOBILE_EXPENSIFY_URL); + section += ` ↳ ${mobileExpensifyUrl}\n`; + } + } + } else { + prIndex++; + const url = GithubUtils.getPullRequestURLFromNumber(entry.prNumber, CONST.APP_REPO_URL); + section += `${prIndex}. ${url}\n`; + } + } + if (mobileExpensifyPRsPendingSubmoduleUpdate.length > 0) { + const sortedPending = [...mobileExpensifyPRsPendingSubmoduleUpdate].sort((a, b) => a.date.localeCompare(b.date)); + section += `\n--- PRs waiting for Mobile-Expensify submodule update\n`; + for (const mobileExpensifyPR of sortedPending) { + const mobileExpensifyUrl = GithubUtils.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST.MOBILE_EXPENSIFY_URL); + section += `${mobileExpensifyUrl}\n`; + } + } + section += '\n
'; + return section; +} + +/** + * Build params for creating a brand-new deploy checklist. + */ +function buildNewChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + chronologicalSection, +}: { + newVersion: string; + newPRNumbers: number[]; + mergedMobileExpensifyPREntries: MergedPR[]; + previousChecklistData: StagingDeployCashData; + chronologicalSection: string; +}) { + return { + tag: newVersion, + PRList: newPRNumbers, + PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => pr.prNumber), + previousTag: previousChecklistData.version, + chronologicalSection, + }; +} + +/** + * Build params for updating an existing open deploy checklist, preserving verified/resolved state. + */ +async function buildUpdateChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + currentChecklistData, + chronologicalSection, +}: { + newVersion: string; + newPRNumbers: number[]; + mergedMobileExpensifyPREntries: MergedPR[]; + previousChecklistData: StagingDeployCashData; + currentChecklistData: StagingDeployCashData; + chronologicalSection: string; +}) { + const PRList = preserveCheckboxState( + newPRNumbers.map((prNum) => ({ + number: prNum, + url: GithubUtils.getPullRequestURLFromNumber(prNum, CONST.APP_REPO_URL), + })), + currentChecklistData.PRList, + 'isVerified', + ); + + const PRListMobileExpensify = preserveCheckboxState( + mergedMobileExpensifyPREntries.map((entry) => ({ + number: entry.prNumber, + url: GithubUtils.getPullRequestURLFromNumber(entry.prNumber, CONST.MOBILE_EXPENSIFY_URL), + })), + currentChecklistData.PRListMobileExpensify, + 'isVerified', + ); + + // Include existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list + for (const existingPR of currentChecklistData.PRListMobileExpensify) { + if (!PRListMobileExpensify.some((pr) => pr.number === existingPR.number)) { + PRListMobileExpensify.push(existingPR); + } + } + + const openDeployBlockers = await GithubUtils.paginate(GithubUtils.octokit.issues.listForRepo, { + log: console, + owner: CONST.GITHUB_OWNER, + repo: CONST.APP_REPO, + labels: CONST.LABELS.DEPLOY_BLOCKER, + }); + + const deployBlockers = preserveCheckboxState( + openDeployBlockers.map((db) => ({number: db.number, url: db.html_url})), + currentChecklistData.deployBlockers, + 'isResolved', + ); + + // Add demoted/closed blockers from current checklist and auto-check them + for (const deployBlocker of currentChecklistData.deployBlockers) { + const isResolved = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); + deployBlockers.push({...deployBlocker, isResolved}); + } + + const didVersionChange = newVersion !== currentChecklistData.version; + + return { + tag: newVersion, + PRList: PRList.map((pr) => pr.number), + PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.number), + verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.number), + verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.number), + deployBlockers: deployBlockers.map((blocker) => blocker.url), + resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), + resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.number), + isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, + isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, + previousTag: previousChecklistData.version, + chronologicalSection, + }; +} + +async function run(): Promise { + // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time + const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')) as PackageJson; + const newVersion = packageJson.version; + const newStagingTag = `${packageJson.version}-staging`; + + try { + const {data: recentDeployChecklists} = await GithubUtils.octokit.issues.listForRepo({ + log: console, + owner: CONST.GITHUB_OWNER, + repo: CONST.APP_REPO, + labels: CONST.LABELS.STAGING_DEPLOY, + state: 'all', + }); + + const mostRecentChecklist = recentDeployChecklists.at(0); + if (!mostRecentChecklist) { + throw new Error('Could not find the most recent checklist'); + } + + const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; + const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); + if (shouldCreateNewDeployChecklist) { + console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); + } else { + console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); + } + + if (!previousChecklist) { + throw new Error('Could not find the previous checklist'); + } + + const previousChecklistData = getStagingDeployCashData(previousChecklist); + const currentChecklistData: StagingDeployCashData | undefined = shouldCreateNewDeployChecklist ? undefined : getStagingDeployCashData(mostRecentChecklist); + + const {mergedPRs: mergedPREntries, submoduleUpdates} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.APP_REPO); + const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); + + const previousPRNumbers = new Set(previousChecklistData.PRList.map((pr) => pr.number)); + const previousMobileExpensifyPRNumbers = new Set(previousChecklistData.PRListMobileExpensify.map((pr) => pr.number)); + core.startGroup('Filtering PRs:'); + core.info('mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out'); + core.info(`Found ${previousPRNumbers.size} PRs in the previous checklist:`); + core.info(JSON.stringify(Array.from(previousPRNumbers))); + const newPRNumbers = mergedPRs.filter((prNum) => !previousPRNumbers.has(prNum)); + core.info(`Found ${newPRNumbers.length} PRs deployed since the previous checklist:`); + core.info(JSON.stringify(newPRNumbers)); + + const removedPRs = mergedPRs.filter((prNum) => previousPRNumbers.has(prNum)); + if (removedPRs.length > 0) { + core.info(`⚠️⚠️ Filtered out the following cherry-picked PRs that were released with the previous checklist: ${removedPRs.join(', ')} ⚠️⚠️`); + } + core.endGroup(); + console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); + + let mergedMobileExpensifyPREntries: MergedPR[] = []; + try { + const {mergedPRs: allMobileExpensifyPREntries} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.MOBILE_EXPENSIFY_REPO); + mergedMobileExpensifyPREntries = allMobileExpensifyPREntries.filter((pr) => !previousMobileExpensifyPRNumbers.has(pr.prNumber)); + + const allCount = allMobileExpensifyPREntries.length; + const newCount = mergedMobileExpensifyPREntries.length; + console.info(`Found ${allCount} total Mobile-Expensify PRs, ${newCount} new ones after filtering:`); + console.info(`Mobile-Expensify PRs: ${mergedMobileExpensifyPREntries.map((pr) => pr.prNumber).join(', ')}`); + + const removedMobileExpensifyPRs = allMobileExpensifyPREntries.filter((pr) => previousMobileExpensifyPRNumbers.has(pr.prNumber)); + if (removedMobileExpensifyPRs.length > 0) { + core.info( + `⚠️⚠️ Filtered out the following cherry-picked Mobile-Expensify PRs that were released with the previous checklist: ${removedMobileExpensifyPRs.map((pr) => pr.prNumber).join(', ')} ⚠️⚠️`, + ); + } + } catch (error) { + if (process.env.GITHUB_REPOSITORY !== `${CONST.GITHUB_OWNER}/${CONST.APP_REPO}`) { + console.warn( + "⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessble. This is expected for development/testing on forks.", + ); + } else { + console.error('Failed to fetch Mobile-Expensify PRs:', error); + } + } + + const chronologicalPREntries = mergedPREntries.filter((pr) => !previousPRNumbers.has(pr.prNumber)).sort((a, b) => a.date.localeCompare(b.date)); + const chronologicalSection = await buildChronologicalSection({ + chronologicalPREntries, + submoduleUpdates, + mergedMobileExpensifyPREntries, + }); + + let checklistParams; + if (shouldCreateNewDeployChecklist) { + checklistParams = buildNewChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + chronologicalSection, + }); + } else if (currentChecklistData) { + checklistParams = await buildUpdateChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + currentChecklistData, + chronologicalSection, + }); + } else { + throw new Error('Expected current checklist data for update but it was undefined'); + } + + const {issueBody: checklistBody, issueAssignees: checklistAssignees} = await generateStagingDeployCashBodyAndAssignees(checklistParams); + + const defaultPayload = { + owner: CONST.GITHUB_OWNER, + repo: CONST.APP_REPO, + body: checklistBody, + }; + + if (shouldCreateNewDeployChecklist) { + const {data: newChecklist} = await GithubUtils.octokit.issues.create({ + ...defaultPayload, + title: `Deploy Checklist: New Expensify ${format(new Date(), CONST.DATE_FORMAT_STRING)}`, + labels: [CONST.LABELS.STAGING_DEPLOY, CONST.LABELS.LOCK_DEPLOY], + assignees: [CONST.APPLAUSE_BOT as string].concat(checklistAssignees), + }); + console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); + return newChecklist; + } + + const {data: updatedChecklist} = await GithubUtils.octokit.issues.update({ + ...defaultPayload, + issue_number: currentChecklistData?.number ?? 0, + }); + console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); + return updatedChecklist; + } catch (err: unknown) { + console.error('An unknown error occurred!', err); + core.setFailed(err as Error); + } +} + +if (require.main === module) { + run(); +} + +export default run; +export {buildChronologicalSection, buildNewChecklistParams, buildUpdateChecklistParams, preserveCheckboxState}; diff --git a/tests/unit/GithubUtilsTest.ts b/tests/unit/GithubUtilsTest.ts index b65337e1480aa..c1333e3cabbec 100644 --- a/tests/unit/GithubUtilsTest.ts +++ b/tests/unit/GithubUtilsTest.ts @@ -6,31 +6,12 @@ import * as core from '@actions/core'; import {RequestError} from '@octokit/request-error'; import type {Writable} from 'type-fest'; import CONST from '@github/libs/CONST'; -import type {InternalOctokit, ListForRepoMethod} from '@github/libs/GithubUtils'; +import type {InternalOctokit} from '@github/libs/GithubUtils'; import GithubUtils from '@github/libs/GithubUtils'; const mockGetInput = jest.fn(); const mockListIssues = jest.fn(); -type Label = { - id: number; - number?: number; - isVerified?: boolean; - node_id: string; - url: string; - name: string; - color: string; - default: boolean; - description: string; -}; - -type Issue = { - url: string; - title: string; - labels: Label[]; - body: string; -}; - type ObjectMethodData = { data: T; }; @@ -70,145 +51,6 @@ afterEach(() => { }); describe('GithubUtils', () => { - describe('getStagingDeployCash', () => { - const baseIssue: Issue = { - url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', - title: 'Andrew Test Issue', - labels: [ - { - id: 2783847782, - // cspell:disable-next-line - node_id: 'MDU6TGFiZWwyNzgzODQ3Nzgy', - url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/labels/StagingDeployCash', - name: 'StagingDeployCash', - color: '6FC269', - default: false, - description: '', - }, - ], - // eslint-disable-next-line max-len - body: `**Release Version:** \`1.0.1-47\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21\r\n- [x] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/22\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/23\r\n\r\n`, - }; - const issueWithDeployBlockers = {...baseIssue}; - // eslint-disable-next-line max-len - issueWithDeployBlockers.body += `\r\n**Deploy Blockers:**\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1\r\n- [x] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234\r\n`; - - const baseExpectedResponse: Partial>> = { - PRList: [ - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21`, - number: 21, - isVerified: false, - }, - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/22`, - number: 22, - isVerified: true, - }, - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/23`, - number: 23, - isVerified: false, - }, - ], - PRListMobileExpensify: [], - labels: [ - { - color: '6FC269', - default: false, - description: '', - id: 2783847782, - name: 'StagingDeployCash', - // cspell:disable-next-line - node_id: 'MDU6TGFiZWwyNzgzODQ3Nzgy', - url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/labels/StagingDeployCash', - }, - ], - version: '1.0.1-47', - tag: '1.0.1-47-staging', - title: 'Andrew Test Issue', - url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', - number: 29, - deployBlockers: [], - internalQAPRList: [], - isSentryChecked: false, - isGHStatusChecked: false, - }; - const expectedResponseWithDeployBlockers = {...baseExpectedResponse}; - expectedResponseWithDeployBlockers.deployBlockers = [ - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1`, - number: 1, - isResolved: false, - }, - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2`, - number: 2, - isResolved: true, - }, - { - url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234`, - number: 1234, - isResolved: false, - }, - ]; - - test('Test finding an open issue with no PRs successfully', () => { - const bareIssue: Issue = { - ...baseIssue, - // eslint-disable-next-line max-len - body: `**Release Version:** \`1.0.1-47\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\ncc @Expensify/applauseleads\n`, - }; - - const bareExpectedResponse: Partial>> = { - ...baseExpectedResponse, - PRList: [], - PRListMobileExpensify: [], - }; - - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [bareIssue]}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().then((data) => expect(data).toStrictEqual(bareExpectedResponse)); - }); - - test('Test finding an open issue successfully', () => { - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [baseIssue]}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().then((data) => expect(data).toStrictEqual(baseExpectedResponse)); - }); - - test('Test finding an open issue successfully and parsing with deploy blockers', () => { - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [issueWithDeployBlockers]}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); - }); - - test('Test finding an open issue successfully and parsing with blockers w/o carriage returns', () => { - const modifiedIssueWithDeployBlockers = {...issueWithDeployBlockers}; - modifiedIssueWithDeployBlockers.body = modifiedIssueWithDeployBlockers.body.replaceAll('\r', ''); - - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({ - data: [modifiedIssueWithDeployBlockers], - }) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); - }); - - test('Test finding an open issue without a body', () => { - const noBodyIssue = baseIssue; - noBodyIssue.body = ''; - - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [noBodyIssue]}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue with correct data.'))); - }); - - test('Test finding more than one issue', () => { - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [{a: 1}, {b: 2}]}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Found more than one StagingDeployCash issue.'))); - }); - - test('Test finding no issues', () => { - GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []}) as unknown as ListForRepoMethod; - return GithubUtils.getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue.'))); - }); - }); - describe('getPullRequestNumberFromURL', () => { describe('valid pull requests', () => { test.each([ @@ -295,382 +137,6 @@ describe('GithubUtils', () => { }); }); - describe('generateStagingDeployCashBody', () => { - const mockTags = [{name: '1.0.2-0'}, {name: '1.0.2-12'}]; - const mockPRs = [ - { - number: 1, - title: 'Test PR 1', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1`, - user: {login: 'username'}, - labels: [], - }, - { - number: 2, - title: 'Test PR 2', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/2`, - user: {login: 'username'}, - labels: [], - }, - { - number: 3, - title: 'Test PR 3', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, - user: {login: 'username'}, - labels: [], - }, - { - number: 4, - title: '[NO QA] Test No QA PR uppercase', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/4`, - user: {login: 'username'}, - labels: [], - }, - { - number: 5, - title: '[NoQa] Test No QA PR Title Case', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/5`, - user: {login: 'username'}, - labels: [], - }, - { - number: 6, - title: '[Internal QA] Another Test Internal QA PR', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, - user: {login: 'username'}, - labels: [ - { - id: 1234, - node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=', - url: `https://api.github.com/${process.env.GITHUB_REPOSITORY}/labels/InternalQA`, - name: 'InternalQA', - description: 'An Expensifier needs to test this.', - color: 'f29513', - }, - ], - assignees: [], - }, - { - number: 7, - title: '[Internal QA] Another Test Internal QA PR', - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/7`, - user: {login: 'username'}, - labels: [ - { - id: 1234, - node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=', - url: `https://api.github.com/${process.env.GITHUB_REPOSITORY}/labels/InternalQA`, - name: 'InternalQA', - description: 'An Expensifier needs to test this.', - color: 'f29513', - }, - ], - assignees: [], - }, - ]; - const mockInternalQaPR = { - merged_by: {login: 'octocat'}, - }; - const mockGithub = jest.fn(() => ({ - getOctokit: () => ({ - rest: { - repos: { - listTags: jest.fn().mockResolvedValue({data: mockTags}), - }, - pulls: { - list: jest.fn().mockResolvedValue({data: mockPRs}), - get: jest.fn().mockResolvedValue({data: mockInternalQaPR}), - }, - }, - paginate: jest.fn().mockImplementation((objectMethod: () => Promise>) => objectMethod().then(({data}) => data)), - }), - })); - - const octokit = mockGithub().getOctokit(); - const githubUtils = class extends GithubUtils {}; - githubUtils.internalOctokit = octokit as unknown as InternalOctokit; - const tag = '1.0.2-12'; - const basePRList = [ - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/2`, - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1`, - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, // This is an intentional duplicate for testing duplicates - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/4`, // No QA - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/5`, // No QA - ]; - const PRListMobileExpensify = [ - `https://github.com/Expensify/Mobile-Expensify/pull/1`, - `https://github.com/Expensify/Mobile-Expensify/pull/2`, - `https://github.com/Expensify/Mobile-Expensify/pull/3`, - ]; - const internalQAPRList = [ - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, // Internal QA - `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/7`, // Internal QA - ]; - - const baseDeployBlockerList = [`https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/4`]; - - // eslint-disable-next-line max-len - const baseExpectedOutput = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n`; - const openCheckbox = '- [ ] '; - const closedCheckbox = '- [x] '; - const ccApplauseLeads = 'cc @Expensify/applauseleads\r\n'; - const deployBlockerHeader = '\r\n**Deploy Blockers:**'; - const internalQAHeader = '\r\n\r\n**Internal QA:**'; - const lineBreak = '\r\n'; - const lineBreakDouble = '\r\n\r\n'; - const assignOctocat = ' - @octocat'; - const deployerVerificationsHeader = '\r\n**Deployer verifications:**'; - // eslint-disable-next-line max-len - const sentryVerificationCurrentRelease = `I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - const sentryVerificationPreviousRelease = `I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - const ghVerification = 'I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.'; - - // Valid output which will be reused in the deploy blocker tests - const allVerifiedExpectedOutput = - `${baseExpectedOutput}` + - `${closedCheckbox}${basePRList.at(2)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreak}`; - - test('Test no verified PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n`; - expect(issue.issueBody).toBe( - `${expectedOutputWithMobileExpensify}` + - `${openCheckbox}${basePRList.at(2)}` + - `${lineBreak}${openCheckbox}${basePRList.at(0)}` + - `${lineBreak}${openCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreak}${lineBreakDouble}**Mobile-Expensify PRs:**` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(0)}` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(1)}` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(2)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test Mobile-Expensify compare link with Mobile-Expensify PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - // Should include Mobile-Expensify compare link since we have Mobile-Expensify PRs - expect(issue.issueBody).toContain('**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging'); - expect(issue.issueBody).toContain('**Mobile-Expensify PRs:**'); - }); - }); - - test('Test no Mobile-Expensify compare link without Mobile-Expensify PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify: []}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - // Should NOT include Mobile-Expensify compare link since we don't have Mobile-Expensify PRs - expect(issue.issueBody).not.toContain('**Mobile-Expensify Changes:**'); - expect(issue.issueBody).not.toContain('**Mobile-Expensify PRs:**'); // And should not have PRs section either - }); - }); - - test('Test some verified PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: [basePRList.at(0) ?? '']}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - - expect(issue.issueBody).toBe( - `${baseExpectedOutput}` + - `${openCheckbox}${basePRList.at(2)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + - `${lineBreak}${openCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test all verified PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - - expect(issue.issueBody).toBe( - `${allVerifiedExpectedOutput}` + - `${lineBreak}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test no resolved deploy blockers', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList, deployBlockers: baseDeployBlockerList}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - - expect(issue.issueBody).toBe( - `${allVerifiedExpectedOutput}` + - `${lineBreak}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}${lineBreak}` + - `${lineBreak}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test some resolved deploy blockers', () => { - githubUtils - .generateStagingDeployCashBodyAndAssignees({ - tag, - PRList: basePRList, - verifiedPRList: basePRList, - deployBlockers: baseDeployBlockerList, - resolvedDeployBlockers: [baseDeployBlockerList.at(0) ?? ''], - }) - .then((issue) => { - if (typeof issue !== 'object') { - return; - } - - expect(issue.issueBody).toBe( - `${allVerifiedExpectedOutput}` + - `${lineBreak}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test all resolved deploy blockers', () => { - githubUtils - .generateStagingDeployCashBodyAndAssignees({ - tag, - PRList: basePRList, - verifiedPRList: basePRList, - deployBlockers: baseDeployBlockerList, - resolvedDeployBlockers: baseDeployBlockerList, - }) - .then((issue) => { - if (typeof issue !== 'object') { - return; - } - expect(issue.issueBody).toBe( - `${baseExpectedOutput}` + - `${closedCheckbox}${basePRList.at(2)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(0)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(1)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual([]); - }); - }); - - test('Test internalQA PRs', () => { - githubUtils.generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], PRListMobileExpensify}).then((issue) => { - if (typeof issue !== 'object') { - return; - } - - const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n`; - expect(issue.issueBody).toBe( - `${expectedOutputWithMobileExpensify}` + - `${openCheckbox}${basePRList.at(2)}` + - `${lineBreak}${openCheckbox}${basePRList.at(0)}` + - `${lineBreak}${openCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreak}${lineBreakDouble}**Mobile-Expensify PRs:**` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(0)}` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(1)}` + - `${lineBreak}${openCheckbox}${PRListMobileExpensify.at(2)}` + - `${lineBreak}${internalQAHeader}` + - `${lineBreak}${openCheckbox}${internalQAPRList.at(0)}${assignOctocat}` + - `${lineBreak}${openCheckbox}${internalQAPRList.at(1)}${assignOctocat}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual(['octocat']); - }); - }); - - test('Test some verified internalQA PRs', () => { - githubUtils - .generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], resolvedInternalQAPRs: [internalQAPRList.at(0) ?? '']}) - .then((issue) => { - if (typeof issue !== 'object') { - return; - } - - expect(issue.issueBody).toBe( - `${baseExpectedOutput}` + - `${openCheckbox}${basePRList.at(2)}` + - `${lineBreak}${openCheckbox}${basePRList.at(0)}` + - `${lineBreak}${openCheckbox}${basePRList.at(1)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(4)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreak}${internalQAHeader}` + - `${lineBreak}${closedCheckbox}${internalQAPRList.at(0)}${assignOctocat}` + - `${lineBreak}${openCheckbox}${internalQAPRList.at(1)}${assignOctocat}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + - `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + - `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + - `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, - ); - expect(issue.issueAssignees).toEqual(['octocat']); - }); - }); - }); - const commitHistoryData = { emptyResponse: { data: { diff --git a/tests/unit/StagingDeployUtilsTest.ts b/tests/unit/StagingDeployUtilsTest.ts new file mode 100644 index 0000000000000..4fa7b8800df85 --- /dev/null +++ b/tests/unit/StagingDeployUtilsTest.ts @@ -0,0 +1,516 @@ +/** + * @jest-environment node + */ +/* eslint-disable @typescript-eslint/naming-convention */ +import * as core from '@actions/core'; +import type {Writable} from 'type-fest'; +import type {InternalOctokit, ListForRepoMethod} from '@github/libs/GithubUtils'; +import GithubUtils from '@github/libs/GithubUtils'; +import {generateStagingDeployCashBodyAndAssignees, getStagingDeployCash} from '@github/libs/StagingDeployUtils'; + +const mockGetInput = jest.fn(); +const mockListIssues = jest.fn(); + +type Label = { + id: number; + number?: number; + isVerified?: boolean; + node_id: string; + url: string; + name: string; + color: string; + default: boolean; + description: string; +}; + +type Issue = { + url: string; + title: string; + labels: Label[]; + body: string; +}; + +type ObjectMethodData = { + data: T; +}; + +type OctokitCreateIssue = InternalOctokit['rest']['issues']['create']; + +const asMutable = (value: T): Writable => value as Writable; + +beforeAll(() => { + asMutable(core).getInput = mockGetInput; + + const mockOctokit = { + rest: { + issues: { + create: jest.fn().mockImplementation((arg: Parameters[0]) => + Promise.resolve({ + data: { + ...arg, + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, + }, + }), + ), + listForRepo: mockListIssues, + }, + }, + paginate: jest.fn().mockImplementation((objectMethod: () => Promise>) => objectMethod().then(({data}) => data)), + } as unknown as InternalOctokit; + + GithubUtils.internalOctokit = mockOctokit; +}); + +afterEach(() => { + mockGetInput.mockClear(); + mockListIssues.mockClear(); +}); + +describe('StagingDeployUtils', () => { + describe('getStagingDeployCash', () => { + const baseIssue: Issue = { + url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', + title: 'Andrew Test Issue', + labels: [ + { + id: 2783847782, + // cspell:disable-next-line + node_id: 'MDU6TGFiZWwyNzgzODQ3Nzgy', + url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/labels/StagingDeployCash', + name: 'StagingDeployCash', + color: '6FC269', + default: false, + description: '', + }, + ], + // eslint-disable-next-line max-len + body: `**Release Version:** \`1.0.1-47\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\n**This release contains changes from the following pull requests:**\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21\r\n- [x] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/22\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/23\r\n\r\n`, + }; + const issueWithDeployBlockers = {...baseIssue}; + // eslint-disable-next-line max-len + issueWithDeployBlockers.body += `\r\n**Deploy Blockers:**\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1\r\n- [x] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234\r\n`; + + const baseExpectedResponse: Partial>> = { + PRList: [ + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21`, + number: 21, + isVerified: false, + }, + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/22`, + number: 22, + isVerified: true, + }, + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/23`, + number: 23, + isVerified: false, + }, + ], + PRListMobileExpensify: [], + labels: [ + { + color: '6FC269', + default: false, + description: '', + id: 2783847782, + name: 'StagingDeployCash', + // cspell:disable-next-line + node_id: 'MDU6TGFiZWwyNzgzODQ3Nzgy', + url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/labels/StagingDeployCash', + }, + ], + version: '1.0.1-47', + tag: '1.0.1-47-staging', + title: 'Andrew Test Issue', + url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', + number: 29, + deployBlockers: [], + internalQAPRList: [], + isSentryChecked: false, + isGHStatusChecked: false, + }; + const expectedResponseWithDeployBlockers = {...baseExpectedResponse}; + expectedResponseWithDeployBlockers.deployBlockers = [ + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1`, + number: 1, + isResolved: false, + }, + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2`, + number: 2, + isResolved: true, + }, + { + url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234`, + number: 1234, + isResolved: false, + }, + ]; + + test('Test finding an open issue with no PRs successfully', () => { + const bareIssue: Issue = { + ...baseIssue, + // eslint-disable-next-line max-len + body: `**Release Version:** \`1.0.1-47\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\ncc @Expensify/applauseleads\n`, + }; + + const bareExpectedResponse: Partial>> = { + ...baseExpectedResponse, + PRList: [], + PRListMobileExpensify: [], + }; + + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [bareIssue]}) as unknown as ListForRepoMethod; + return getStagingDeployCash().then((data) => expect(data).toStrictEqual(bareExpectedResponse)); + }); + + test('Test finding an open issue successfully', () => { + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [baseIssue]}) as unknown as ListForRepoMethod; + return getStagingDeployCash().then((data) => expect(data).toStrictEqual(baseExpectedResponse)); + }); + + test('Test finding an open issue successfully and parsing with deploy blockers', () => { + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [issueWithDeployBlockers]}) as unknown as ListForRepoMethod; + return getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); + }); + + test('Test finding an open issue successfully and parsing with blockers w/o carriage returns', () => { + const modifiedIssueWithDeployBlockers = {...issueWithDeployBlockers}; + modifiedIssueWithDeployBlockers.body = modifiedIssueWithDeployBlockers.body.replaceAll('\r', ''); + + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({ + data: [modifiedIssueWithDeployBlockers], + }) as unknown as ListForRepoMethod; + return getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); + }); + + test('Test finding an open issue without a body', () => { + const noBodyIssue = baseIssue; + noBodyIssue.body = ''; + + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [noBodyIssue]}) as unknown as ListForRepoMethod; + return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue with correct data.'))); + }); + + test('Test finding more than one issue', () => { + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [{a: 1}, {b: 2}]}) as unknown as ListForRepoMethod; + return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Found more than one StagingDeployCash issue.'))); + }); + + test('Test finding no issues', () => { + GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []}) as unknown as ListForRepoMethod; + return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue.'))); + }); + }); + + describe('generateStagingDeployCashBody', () => { + const mockPRs = [ + { + number: 1, + title: 'Test PR 1', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1`, + user: {login: 'username'}, + labels: [], + }, + { + number: 2, + title: 'Test PR 2', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/2`, + user: {login: 'username'}, + labels: [], + }, + { + number: 3, + title: 'Test PR 3', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, + user: {login: 'username'}, + labels: [], + }, + { + number: 4, + title: '[NO QA] Test No QA PR uppercase', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/4`, + user: {login: 'username'}, + labels: [], + }, + { + number: 5, + title: '[NoQa] Test No QA PR Title Case', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/5`, + user: {login: 'username'}, + labels: [], + }, + { + number: 6, + title: '[Internal QA] Another Test Internal QA PR', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, + user: {login: 'username'}, + labels: [ + { + id: 1234, + node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=', + url: `https://api.github.com/${process.env.GITHUB_REPOSITORY}/labels/InternalQA`, + name: 'InternalQA', + description: 'An Expensifier needs to test this.', + color: 'f29513', + }, + ], + assignees: [], + }, + { + number: 7, + title: '[Internal QA] Another Test Internal QA PR', + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/7`, + user: {login: 'username'}, + labels: [ + { + id: 1234, + node_id: 'MDU6TGFiZWwyMDgwNDU5NDY=', + url: `https://api.github.com/${process.env.GITHUB_REPOSITORY}/labels/InternalQA`, + name: 'InternalQA', + description: 'An Expensifier needs to test this.', + color: 'f29513', + }, + ], + assignees: [], + }, + ]; + const mockInternalQaPR = { + merged_by: {login: 'octocat'}, + }; + const mockGithub = jest.fn(() => ({ + getOctokit: () => ({ + rest: { + repos: { + listTags: jest.fn().mockResolvedValue({data: [{name: '1.0.2-0'}, {name: '1.0.2-12'}]}), + }, + pulls: { + list: jest.fn().mockResolvedValue({data: mockPRs}), + get: jest.fn().mockResolvedValue({data: mockInternalQaPR}), + }, + }, + paginate: jest.fn().mockImplementation((objectMethod: () => Promise>) => objectMethod().then(({data}) => data)), + }), + })); + + const octokit = mockGithub().getOctokit(); + + beforeEach(() => { + GithubUtils.internalOctokit = octokit as unknown as InternalOctokit; + }); + + const tag = '1.0.2-12'; + const basePRList = [2, 3, 1, 3, 4, 5]; + const PRListMobileExpensify = [1, 2, 3]; + const internalQAPRList = [6, 7]; + + const baseDeployBlockerList = [`https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/4`]; + + const baseExpectedOutput = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; + const openCheckbox = '- [ ] '; + const closedCheckbox = '- [x] '; + const ccApplauseLeads = 'cc @Expensify/applauseleads\n'; + const deployBlockerHeader = '\n**Deploy Blockers:**'; + const internalQAHeader = '\n\n**Internal QA:**'; + const lineBreak = '\n'; + const lineBreakDouble = '\n\n'; + const assignOctocat = ' - @octocat'; + const deployerVerificationsHeader = '\n**Deployer verifications:**'; + const sentryVerificationCurrentRelease = `I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; + const sentryVerificationPreviousRelease = `I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; + const ghVerification = 'I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.'; + + const prURL = (n: number) => `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${n}`; + const mobileURL = (n: number) => `https://github.com/Expensify/Mobile-Expensify/pull/${n}`; + + const allVerifiedExpectedOutput = + `${baseExpectedOutput}` + + `${closedCheckbox}${prURL(1)}` + + `${lineBreak}${closedCheckbox}${prURL(2)}` + + `${lineBreak}${closedCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${lineBreak}`; + + test('Test no verified PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); + const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; + expect(issue.issueBody).toBe( + `${expectedOutputWithMobileExpensify}` + + `${openCheckbox}${prURL(1)}` + + `${lineBreak}${openCheckbox}${prURL(2)}` + + `${lineBreak}${openCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${lineBreakDouble}**Mobile-Expensify PRs:**` + + `${lineBreak}${openCheckbox}${mobileURL(1)}` + + `${lineBreak}${openCheckbox}${mobileURL(2)}` + + `${lineBreak}${openCheckbox}${mobileURL(3)}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test Mobile-Expensify compare link with Mobile-Expensify PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); + expect(issue.issueBody).toContain('**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging'); + expect(issue.issueBody).toContain('**Mobile-Expensify PRs:**'); + }); + + test('Test no Mobile-Expensify compare link without Mobile-Expensify PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify: []}); + expect(issue.issueBody).not.toContain('**Mobile-Expensify Changes:**'); + expect(issue.issueBody).not.toContain('**Mobile-Expensify PRs:**'); + }); + + test('Test some verified PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: [2]}); + expect(issue.issueBody).toBe( + `${baseExpectedOutput}` + + `${openCheckbox}${prURL(1)}` + + `${lineBreak}${closedCheckbox}${prURL(2)}` + + `${lineBreak}${openCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test all verified PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList}); + expect(issue.issueBody).toBe( + `${allVerifiedExpectedOutput}` + + `${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test no resolved deploy blockers', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList, deployBlockers: baseDeployBlockerList}); + expect(issue.issueBody).toBe( + `${allVerifiedExpectedOutput}` + + `${deployBlockerHeader}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test some resolved deploy blockers', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({ + tag, + PRList: basePRList, + verifiedPRList: basePRList, + deployBlockers: baseDeployBlockerList, + resolvedDeployBlockers: [baseDeployBlockerList.at(0) ?? ''], + }); + expect(issue.issueBody).toBe( + `${allVerifiedExpectedOutput}` + + `${deployBlockerHeader}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test all resolved deploy blockers', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({ + tag, + PRList: basePRList, + verifiedPRList: basePRList, + deployBlockers: baseDeployBlockerList, + resolvedDeployBlockers: baseDeployBlockerList, + }); + expect(issue.issueBody).toBe( + `${baseExpectedOutput}` + + `${closedCheckbox}${prURL(1)}` + + `${lineBreak}${closedCheckbox}${prURL(2)}` + + `${lineBreak}${closedCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${lineBreak}${deployBlockerHeader}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + + `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual([]); + }); + + test('Test internalQA PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], PRListMobileExpensify}); + const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; + expect(issue.issueBody).toBe( + `${expectedOutputWithMobileExpensify}` + + `${openCheckbox}${prURL(1)}` + + `${lineBreak}${openCheckbox}${prURL(2)}` + + `${lineBreak}${openCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${lineBreakDouble}**Mobile-Expensify PRs:**` + + `${lineBreak}${openCheckbox}${mobileURL(1)}` + + `${lineBreak}${openCheckbox}${mobileURL(2)}` + + `${lineBreak}${openCheckbox}${mobileURL(3)}` + + `${internalQAHeader}` + + `${lineBreak}${openCheckbox}${prURL(6)}${assignOctocat}` + + `${lineBreak}${openCheckbox}${prURL(7)}${assignOctocat}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual(['octocat']); + }); + + test('Test some verified internalQA PRs', async () => { + const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], resolvedInternalQAPRs: [6]}); + expect(issue.issueBody).toBe( + `${baseExpectedOutput}` + + `${openCheckbox}${prURL(1)}` + + `${lineBreak}${openCheckbox}${prURL(2)}` + + `${lineBreak}${openCheckbox}${prURL(3)}` + + `${lineBreak}${closedCheckbox}${prURL(4)}` + + `${lineBreak}${closedCheckbox}${prURL(5)}` + + `${internalQAHeader}` + + `${lineBreak}${closedCheckbox}${prURL(6)}${assignOctocat}` + + `${lineBreak}${openCheckbox}${prURL(7)}${assignOctocat}` + + `${lineBreak}${deployerVerificationsHeader}` + + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + + `${lineBreak}${openCheckbox}${ghVerification}` + + `${lineBreak}${ccApplauseLeads}`, + ); + expect(issue.issueAssignees).toEqual(['octocat']); + }); + }); +}); diff --git a/tests/unit/createOrUpdateStagingDeployTest.ts b/tests/unit/createOrUpdateStagingDeployTest.ts index 8ba84a35e5482..d3dea3f03fde5 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.ts +++ b/tests/unit/createOrUpdateStagingDeployTest.ts @@ -2,11 +2,12 @@ import * as core from '@actions/core'; import * as fns from 'date-fns'; import {vol} from 'memfs'; import path from 'path'; -import run from '@github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy'; import CONST from '@github/libs/CONST'; import type {InternalOctokit} from '@github/libs/GithubUtils'; import GithubUtils from '@github/libs/GithubUtils'; import GitUtils from '@github/libs/GitUtils'; +import * as StagingDeployUtils from '@github/libs/StagingDeployUtils'; +import run from '@scripts/createOrUpdateStagingDeploy'; /** * @jest-environment node @@ -142,7 +143,7 @@ const baseIssueList = [`https://github.com/${process.env.GITHUB_REPOSITORY}/issu // eslint-disable-next-line max-len const baseExpectedOutput = (version = '1.0.2-1', includeMobileExpensifyCompare = true) => // cspell:disable - `**Release Version:** \`${version}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n${includeMobileExpensifyCompare ? `**Mobile-Expensify Changes:** https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n` : ''}\r\n**This release contains changes from the following pull requests:**\r\n`; + `**Release Version:** \`${version}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n${includeMobileExpensifyCompare ? `**Mobile-Expensify Changes:** https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n` : ''}\n**This release contains changes from the following pull requests:**\n`; // cspell:enable const openCheckbox = '- [ ] '; const closedCheckbox = '- [x] '; @@ -155,10 +156,10 @@ const sentryVerificationPreviousRelease = (version: string) => `I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${version}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; // eslint-disable-next-line max-len const ghVerification = 'I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.'; -const ccApplauseLeads = `cc @Expensify/applauseleads\r\n`; +const ccApplauseLeads = `cc @Expensify/applauseleads\n`; const deployBlockerHeader = '**Deploy Blockers:**'; -const lineBreak = '\r\n'; -const lineBreakDouble = '\r\n\r\n'; +const lineBreak = '\n'; +const lineBreakDouble = '\n\n'; describe('createOrUpdateStagingDeployCash', () => { const closedStagingDeployCash = { @@ -208,30 +209,30 @@ describe('createOrUpdateStagingDeployCash', () => { return ''; } - let section = '
\r\nChronologically ordered merged PRs (oldest first)\r\n\r\n'; + let section = '
\nChronologically ordered merged PRs (oldest first)\n\n'; let prIndex = 0; for (const entry of normalizedEntries) { if (entry.type === 'submodule') { prIndex++; const buildLink = entry.buildLink ? ` — [Adhoc Build](${entry.buildLink})` : ` — ${(entry.commit ?? '').substring(0, 7)}`; - section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\r\n`; + section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\n`; if (entry.mobileExpensifyPRs) { for (const mobileExpensifyPR of entry.mobileExpensifyPRs) { - section += ` ↳ https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/${mobileExpensifyPR}\r\n`; + section += ` ↳ https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/${mobileExpensifyPR}\n`; } } } else { prIndex++; - section += `${prIndex}. https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${entry.prNumber}\r\n`; + section += `${prIndex}. https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${entry.prNumber}\n`; } } if (pendingMobileExpensifyPRs.length > 0) { - section += `\r\n--- PRs waiting for Mobile-Expensify submodule update\r\n`; + section += `\n--- PRs waiting for Mobile-Expensify submodule update\n`; for (const mobileExpensifyPR of pendingMobileExpensifyPRs) { - section += `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/${mobileExpensifyPR}\r\n`; + section += `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/${mobileExpensifyPR}\n`; } } - section += '\r\n
'; + section += '\n
'; return section; } @@ -272,17 +273,17 @@ describe('createOrUpdateStagingDeployCash', () => { `${baseExpectedOutput()}` + `${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${openCheckbox}${basePRList.at(6)}` + - `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + `${lineBreakDouble}**Mobile-Expensify PRs:**` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(0)}` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(1)}` + - `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}${lineBreak}` + + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}` + `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests, [20, 21, 22])}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease('1.0.1-0')}` + `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, + `${lineBreak}${ccApplauseLeads}`, }); }); @@ -323,14 +324,14 @@ describe('createOrUpdateStagingDeployCash', () => { `${baseExpectedOutput('1.0.2-1', false)}` + `${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${openCheckbox}${basePRList.at(6)}` + - `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + // Note: No Mobile-Expensify PRs section since there are none `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease('1.0.1-0')}` + `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, + `${lineBreak}${ccApplauseLeads}`, }); }); @@ -439,25 +440,25 @@ describe('createOrUpdateStagingDeployCash', () => { `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + - `${lineBreak}${openCheckbox}${basePRList.at(9)}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(9)}` + `${lineBreakDouble}**Mobile-Expensify PRs:**` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(0)}` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(1)}` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}` + `${lineBreak}${openCheckbox}https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/23` + - `${lineBreak}${openCheckbox}https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/24${lineBreak}` + + `${lineBreak}${openCheckbox}https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/24` + `${lineBreakDouble}${deployBlockerHeader}` + `${lineBreak}${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(1)}${lineBreak}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(1)}` + `${lineBreakDouble}${buildChronologicalSection([...baseNewPullRequests, ...newPullRequests], [20, 21, 22, 23, 24])}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease('1.0.2-2')}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease('1.0.1-0')}` + `${lineBreak}${openCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, + `${lineBreak}${ccApplauseLeads}`, }); }); @@ -517,23 +518,23 @@ describe('createOrUpdateStagingDeployCash', () => { `${baseExpectedOutput('1.0.2-1')}` + `${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + - `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + `${lineBreakDouble}**Mobile-Expensify PRs:**` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(0)}` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(1)}` + - `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}${lineBreak}` + + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}` + `${lineBreakDouble}${deployBlockerHeader}` + `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(1)}${lineBreak}` + + `${lineBreak}${openCheckbox}${baseIssueList.at(1)}` + `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests, [20, 21, 22])}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + `${lineBreak}${closedCheckbox}${sentryVerificationPreviousRelease('1.0.1-0')}` + `${lineBreak}${closedCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, + `${lineBreak}${ccApplauseLeads}`, }); }); @@ -577,18 +578,18 @@ describe('createOrUpdateStagingDeployCash', () => { `${baseExpectedOutput('1.0.2-1', false)}` + `${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${closedCheckbox}${basePRList.at(6)}` + - `${lineBreak}${openCheckbox}${basePRList.at(7)}${lineBreak}` + + `${lineBreak}${openCheckbox}${basePRList.at(7)}` + // Note: No Mobile-Expensify PRs section since there are none `${lineBreakDouble}${deployBlockerHeader}` + `${lineBreak}${openCheckbox}${basePRList.at(5)}` + `${lineBreak}${openCheckbox}${basePRList.at(8)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(9)}${lineBreak}` + + `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests)}` + - `${lineBreakDouble}${deployerVerificationsHeader}` + + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + `${lineBreak}${closedCheckbox}${sentryVerificationPreviousRelease('1.0.1-0')}` + `${lineBreak}${closedCheckbox}${ghVerification}` + - `${lineBreakDouble}${ccApplauseLeads}`, + `${lineBreak}${ccApplauseLeads}`, }); }); }); @@ -613,7 +614,7 @@ describe('createOrUpdateStagingDeployCash', () => { }); // Mock previous checklist containing PRs 6,8 - const mockGetStagingDeployCashData = jest.spyOn(GithubUtils, 'getStagingDeployCashData'); + const mockGetStagingDeployCashData = jest.spyOn(StagingDeployUtils, 'getStagingDeployCashData'); mockGetStagingDeployCashData.mockImplementation(() => ({ title: 'Previous Checklist', url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, @@ -682,7 +683,7 @@ describe('createOrUpdateStagingDeployCash', () => { }); // Mock previous checklist containing PRs 6,8 but no Mobile-Expensify PRs - const mockGetStagingDeployCashData = jest.spyOn(GithubUtils, 'getStagingDeployCashData'); + const mockGetStagingDeployCashData = jest.spyOn(StagingDeployUtils, 'getStagingDeployCashData'); mockGetStagingDeployCashData.mockImplementation(() => ({ title: 'Previous Checklist', url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, diff --git a/tests/unit/isStagingDeployLockedTest.ts b/tests/unit/isStagingDeployLockedTest.ts index 1e47739faeb3f..8a6aea7b912e8 100644 --- a/tests/unit/isStagingDeployLockedTest.ts +++ b/tests/unit/isStagingDeployLockedTest.ts @@ -3,30 +3,26 @@ */ import * as core from '@actions/core'; import run from '../../.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked'; -import GithubUtils from '../../.github/libs/GithubUtils'; +import * as StagingDeployUtils from '../../.github/libs/StagingDeployUtils'; + +jest.mock('../../.github/libs/StagingDeployUtils'); -// Mock the entire GithubUtils module -jest.mock('../../.github/libs/GithubUtils'); beforeAll(() => { - // Mock required GITHUB_TOKEN process.env.INPUT_GITHUB_TOKEN = 'fake_token'; }); beforeEach(() => { - // Reset mocks before each test jest.resetModules(); }); afterAll(() => { - // Remove mock GITHUB_TOKEN delete process.env.INPUT_GITHUB_TOKEN; }); describe('isStagingDeployLockedTest', () => { describe('GitHub action run function', () => { test('Test returning empty result', () => { - // Mock the return value of GithubUtils.getStagingDeployCash() to return an empty object - GithubUtils.getStagingDeployCash = jest.fn().mockResolvedValue({}); + (StagingDeployUtils.getStagingDeployCash as jest.Mock).mockResolvedValue({}); const setOutputMock = jest.spyOn(core, 'setOutput'); const isStagingDeployLocked = run(); return isStagingDeployLocked.then(() => { @@ -35,13 +31,11 @@ describe('isStagingDeployLockedTest', () => { }); test('Test returning valid locked issue', () => { - // Mocking the minimum amount of data required for a found issue with the correct label const mockData = { labels: [{name: '🔐 LockCashDeploys 🔐'}], }; - // Mock the return value of GithubUtils.getStagingDeployCash() to return the correct label - GithubUtils.getStagingDeployCash = jest.fn().mockResolvedValue(mockData); + (StagingDeployUtils.getStagingDeployCash as jest.Mock).mockResolvedValue(mockData); const setOutputMock = jest.spyOn(core, 'setOutput'); const isStagingDeployLocked = run(); return isStagingDeployLocked.then(() => { From e4508658db942ea461f64fc93318621ad6b5402a Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 20 Feb 2026 15:58:23 -0800 Subject: [PATCH 2/8] Fix CI: spellcheck typo, rebuild action bundles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix "accessble" → "accessible" typo in scripts/createOrUpdateStagingDeploy.ts - Rebuild all GitHub Actions bundles with npm run gh-actions-build Co-authored-by: Cursor --- .../javascript/authorChecklist/index.js | 247 ---- .../javascript/awaitStagingDeploys/index.js | 247 ---- .../javascript/checkAndroidStatus/index.js | 247 ---- .../javascript/checkDeployBlockers/index.js | 233 ---- .../javascript/checkSVGCompression/index.js | 247 ---- .../createOrUpdateStagingDeploy/index.js | 1076 +++++++++-------- .../javascript/formatCodeCovComment/index.js | 247 ---- .../javascript/getArtifactInfo/index.js | 247 ---- .../getDeployPullRequestList/index.js | 247 ---- .../javascript/getPreviousVersion/index.js | 247 ---- .../javascript/getPullRequestDetails/index.js | 233 ---- .../getPullRequestIncrementalChanges/index.js | 247 ---- .../javascript/isStagingDeployLocked/index.js | 470 +++---- .../markPullRequestsAsDeployed/index.js | 247 ---- .../javascript/postTestBuildComment/index.js | 247 ---- .../javascript/proposalPoliceComment/index.js | 247 ---- .../reopenIssueWithComment/index.js | 247 ---- .../javascript/reviewerChecklist/index.js | 247 ---- .../javascript/verifySignedCommits/index.js | 247 ---- .../javascript/waitForPreviousRuns/index.js | 247 ---- scripts/createOrUpdateStagingDeploy.ts | 2 +- 21 files changed, 832 insertions(+), 5134 deletions(-) diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index 7a9cf576f24fc..146d96f3a80a8 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -15684,9 +15684,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -15765,219 +15763,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -16286,38 +16071,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 8534: diff --git a/.github/actions/javascript/awaitStagingDeploys/index.js b/.github/actions/javascript/awaitStagingDeploys/index.js index a53a34f6e4dfc..4cb175fd627c6 100644 --- a/.github/actions/javascript/awaitStagingDeploys/index.js +++ b/.github/actions/javascript/awaitStagingDeploys/index.js @@ -12462,9 +12462,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -12543,219 +12541,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -13064,38 +12849,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9438: diff --git a/.github/actions/javascript/checkAndroidStatus/index.js b/.github/actions/javascript/checkAndroidStatus/index.js index b8f23567060c5..1a6896c30a2b7 100644 --- a/.github/actions/javascript/checkAndroidStatus/index.js +++ b/.github/actions/javascript/checkAndroidStatus/index.js @@ -737205,9 +737205,7 @@ const utils_1 = __nccwpck_require__(73030); const plugin_paginate_rest_1 = __nccwpck_require__(64193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(10537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(17532)); const CONST_1 = __importDefault(__nccwpck_require__(29873)); -const isEmptyObject_1 = __nccwpck_require__(86497); class GithubUtils { static internalOctokit; /** @@ -737286,219 +737284,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -737807,38 +737592,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 17532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 86497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 39491: diff --git a/.github/actions/javascript/checkDeployBlockers/index.js b/.github/actions/javascript/checkDeployBlockers/index.js index cd79ac7b5e495..e50cac55ce6d0 100644 --- a/.github/actions/javascript/checkDeployBlockers/index.js +++ b/.github/actions/javascript/checkDeployBlockers/index.js @@ -11729,9 +11729,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11810,219 +11808,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12331,24 +12116,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - /***/ }), /***/ 6497: diff --git a/.github/actions/javascript/checkSVGCompression/index.js b/.github/actions/javascript/checkSVGCompression/index.js index 9d20ccdb40781..17c73826eaff2 100644 --- a/.github/actions/javascript/checkSVGCompression/index.js +++ b/.github/actions/javascript/checkSVGCompression/index.js @@ -20254,9 +20254,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -20335,219 +20333,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -20856,38 +20641,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 6482: diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js index 6e7d94d886406..272a9fa172d7c 100644 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js @@ -11537,290 +11537,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(2186)); -const format_1 = __nccwpck_require__(2464); -const fs_1 = __importDefault(__nccwpck_require__(7147)); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -const GitUtils_1 = __importDefault(__nccwpck_require__(1547)); -async function buildChronologicalSection({ chronologicalPREntries, submoduleUpdates, mergedMobileExpensifyPREntries, }) { - if (chronologicalPREntries.length === 0 && submoduleUpdates.length === 0 && mergedMobileExpensifyPREntries.length === 0) { - return ''; - } - // Look up workflow run URLs for each submodule update commit - const submoduleRunURLs = new Map(); - const results = await Promise.allSettled(submoduleUpdates.map(async (update) => { - const runURL = await GithubUtils_1.default.getWorkflowRunURLForCommit(update.commit, 'testBuildOnPush.yml'); - return { commit: update.commit, runURL }; - })); - for (const result of results) { - if (result.status === 'fulfilled') { - submoduleRunURLs.set(result.value.commit, result.value.runURL); - } - } - // Group Mobile-Expensify PRs by the submodule update that introduced them. - // A Mobile-Expensify PR is assigned to the first submodule bump whose date >= PR merge date, - // because merging to Mobile-Expensify doesn't matter until the submodule is actually bumped in App. - const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); - const mobileExpensifyPRsBySubmodule = new Map(); - const mobileExpensifyPRsPendingSubmoduleUpdate = []; - for (const mobileExpensifyPR of mergedMobileExpensifyPREntries) { - const matchingUpdate = sortedSubmoduleUpdates.find((update) => update.date.localeCompare(mobileExpensifyPR.date) >= 0); - if (matchingUpdate) { - const existing = mobileExpensifyPRsBySubmodule.get(matchingUpdate.commit) ?? []; - existing.push(mobileExpensifyPR); - mobileExpensifyPRsBySubmodule.set(matchingUpdate.commit, existing); - } - else { - mobileExpensifyPRsPendingSubmoduleUpdate.push(mobileExpensifyPR); - } - } - // Merge PRs and submodule updates into a single chronological timeline - const timeline = [ - ...chronologicalPREntries.map((pr) => ({ type: 'pr', prNumber: pr.prNumber, date: pr.date })), - ...submoduleUpdates.map((update) => ({ type: 'submodule', version: update.version, date: update.date, commit: update.commit })), - ].sort((a, b) => a.date.localeCompare(b.date)); - let section = '
\r\nChronologically ordered merged PRs (oldest first)\r\n\r\n'; - let prIndex = 0; - for (const entry of timeline) { - if (entry.type === 'submodule') { - prIndex++; - const runURL = submoduleRunURLs.get(entry.commit); - const buildLink = runURL ? ` — [Adhoc Build](${runURL})` : ` — ${entry.commit.substring(0, 7)}`; - section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\r\n`; - const mobileExpensifyPRs = mobileExpensifyPRsBySubmodule.get(entry.commit); - if (mobileExpensifyPRs) { - const sortedMobileExpensifyPRs = [...mobileExpensifyPRs].sort((a, b) => a.date.localeCompare(b.date)); - for (const mobileExpensifyPR of sortedMobileExpensifyPRs) { - const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - section += ` ↳ ${mobileExpensifyUrl}\r\n`; - } - } - } - else { - prIndex++; - const url = GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.APP_REPO_URL); - section += `${prIndex}. ${url}\r\n`; - } - } - if (mobileExpensifyPRsPendingSubmoduleUpdate.length > 0) { - const sortedPending = [...mobileExpensifyPRsPendingSubmoduleUpdate].sort((a, b) => a.date.localeCompare(b.date)); - section += `\r\n--- PRs waiting for Mobile-Expensify submodule update\r\n`; - for (const mobileExpensifyPR of sortedPending) { - const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - section += `${mobileExpensifyUrl}\r\n`; - } - } - section += '\r\n
'; - return section; -} -async function run() { - // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time - const packageJson = JSON.parse(fs_1.default.readFileSync('package.json', 'utf8')); - // The checklist will use the package.json version, e.g. '1.2.3-4' - const newVersion = packageJson.version; - // The staging tag will use the package.json version with a '-staging' suffix, e.g. '1.2.3-4-staging' - const newStagingTag = `${packageJson.version}-staging`; +const createOrUpdateStagingDeploy_1 = __importDefault(__nccwpck_require__(4287)); +async function main() { try { - // Start by fetching the list of recent StagingDeployCash issues, along with the list of open deploy blockers - const { data: recentDeployChecklists } = await GithubUtils_1.default.octokit.issues.listForRepo({ - log: console, - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'all', - }); - // Look at the state of the most recent StagingDeployCash, - // if it is open then we'll update the existing one, otherwise, we'll create a new one. - const mostRecentChecklist = recentDeployChecklists.at(0); - if (!mostRecentChecklist) { - throw new Error('Could not find the most recent checklist'); - } - const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; - const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); - if (shouldCreateNewDeployChecklist) { - console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); - } - else { - console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); - } - if (!previousChecklist) { - throw new Error('Could not find the previous checklist'); - } - // Parse the data from the previous and current checklists into the format used to generate the checklist - const previousChecklistData = GithubUtils_1.default.getStagingDeployCashData(previousChecklist); - const currentChecklistData = shouldCreateNewDeployChecklist ? undefined : GithubUtils_1.default.getStagingDeployCashData(mostRecentChecklist); - // Find the list of PRs merged between the current checklist and the previous checklist - const { mergedPRs: mergedPREntries, submoduleUpdates } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.APP_REPO); - const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); - // mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out - const previousPRNumbers = new Set(previousChecklistData.PRList.map((pr) => pr.number)); - const previousMobileExpensifyPRNumbers = new Set(previousChecklistData.PRListMobileExpensify.map((pr) => pr.number)); - core.startGroup('Filtering PRs:'); - core.info('mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out'); - core.info(`Found ${previousPRNumbers.size} PRs in the previous checklist:`); - core.info(JSON.stringify(Array.from(previousPRNumbers))); - const newPRNumbers = mergedPRs.filter((prNum) => !previousPRNumbers.has(prNum)); - core.info(`Found ${newPRNumbers.length} PRs deployed since the previous checklist:`); - core.info(JSON.stringify(newPRNumbers)); - // Filter out cherry-picked PRs that were released with the previous checklist - const removedPRs = mergedPRs.filter((prNum) => previousPRNumbers.has(prNum)); - if (removedPRs.length > 0) { - core.info(`⚠️⚠️ Filtered out the following cherry-picked PRs that were released with the previous checklist: ${removedPRs.join(', ')} ⚠️⚠️`); - } - core.endGroup(); - console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); - // Get merged Mobile-Expensify PRs (with dates for chronological grouping by submodule update) - let mergedMobileExpensifyPREntries = []; - try { - const { mergedPRs: allMobileExpensifyPREntries } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.MOBILE_EXPENSIFY_REPO); - mergedMobileExpensifyPREntries = allMobileExpensifyPREntries.filter((pr) => !previousMobileExpensifyPRNumbers.has(pr.prNumber)); - const allCount = allMobileExpensifyPREntries.length; - const newCount = mergedMobileExpensifyPREntries.length; - console.info(`Found ${allCount} total Mobile-Expensify PRs, ${newCount} new ones after filtering:`); - console.info(`Mobile-Expensify PRs: ${mergedMobileExpensifyPREntries.map((pr) => pr.prNumber).join(', ')}`); - // Log the Mobile-Expensify PRs that were filtered out - const removedMobileExpensifyPRs = allMobileExpensifyPREntries.filter((pr) => previousMobileExpensifyPRNumbers.has(pr.prNumber)); - if (removedMobileExpensifyPRs.length > 0) { - core.info(`⚠️⚠️ Filtered out the following cherry-picked Mobile-Expensify PRs that were released with the previous checklist: ${removedMobileExpensifyPRs.map((pr) => pr.prNumber).join(', ')} ⚠️⚠️`); - } - } - catch (error) { - // Check if this is a forked repository - if (process.env.GITHUB_REPOSITORY !== `${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.APP_REPO}`) { - console.warn("⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessble. This is expected for development/testing on forks."); - } - else { - console.error('Failed to fetch Mobile-Expensify PRs:', error); - } - } - const chronologicalPREntries = mergedPREntries.filter((pr) => !previousPRNumbers.has(pr.prNumber)).sort((a, b) => a.date.localeCompare(b.date)); - const chronologicalSection = await buildChronologicalSection({ - chronologicalPREntries, - submoduleUpdates, - mergedMobileExpensifyPREntries, - }); - // Next, we generate the checklist body - let checklistBody = ''; - let checklistAssignees = []; - if (shouldCreateNewDeployChecklist) { - const stagingDeployCashBodyAndAssignees = await GithubUtils_1.default.generateStagingDeployCashBodyAndAssignees({ - tag: newVersion, - PRList: newPRNumbers.map((value) => GithubUtils_1.default.getPullRequestURLFromNumber(value, CONST_1.default.APP_REPO_URL)), - PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => GithubUtils_1.default.getPullRequestURLFromNumber(pr.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL)), - previousTag: previousChecklistData.version, - chronologicalSection, - }); - if (stagingDeployCashBodyAndAssignees) { - checklistBody = stagingDeployCashBodyAndAssignees.issueBody; - checklistAssignees = stagingDeployCashBodyAndAssignees.issueAssignees.filter(Boolean); - } - } - else { - // Generate the updated PR list, preserving the previous state of `isVerified` for existing PRs - const PRList = newPRNumbers.map((prNum) => { - const indexOfPRInCurrentChecklist = currentChecklistData?.PRList.findIndex((pr) => pr.number === prNum) ?? -1; - const isVerified = indexOfPRInCurrentChecklist >= 0 ? currentChecklistData?.PRList[indexOfPRInCurrentChecklist].isVerified : false; - return { - number: prNum, - url: GithubUtils_1.default.getPullRequestURLFromNumber(prNum, CONST_1.default.APP_REPO_URL), - isVerified, - }; - }); - // Generate the updated Mobile-Expensify PR list, preserving the previous state of `isVerified` for existing PRs - const PRListMobileExpensify = mergedMobileExpensifyPREntries.map((entry) => { - const indexOfPRInCurrentChecklist = currentChecklistData?.PRListMobileExpensify.findIndex((pr) => pr.number === entry.prNumber) ?? -1; - const isVerified = indexOfPRInCurrentChecklist >= 0 ? currentChecklistData?.PRListMobileExpensify[indexOfPRInCurrentChecklist].isVerified : false; - return { - number: entry.prNumber, - url: GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL), - isVerified, - }; - }); - // Generate the deploy blocker list, preserving the previous state of `isResolved` - const openDeployBlockers = await GithubUtils_1.default.paginate(GithubUtils_1.default.octokit.issues.listForRepo, { - log: console, - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.DEPLOY_BLOCKER, - }); - // First, make sure we include all current deploy blockers - const deployBlockers = openDeployBlockers.map((deployBlocker) => { - const indexInCurrentChecklist = currentChecklistData?.deployBlockers.findIndex((item) => item.number === deployBlocker.number) ?? -1; - const isResolved = indexInCurrentChecklist >= 0 ? currentChecklistData?.deployBlockers[indexInCurrentChecklist].isResolved : false; - return { - number: deployBlocker.number, - url: deployBlocker.html_url, - isResolved, - }; - }); - // Then make sure we include any demoted or closed blockers as well, and just check them off automatically - for (const deployBlocker of currentChecklistData?.deployBlockers ?? []) { - const isResolved = deployBlockers.findIndex((openBlocker) => openBlocker.number === deployBlocker.number) < 0; - deployBlockers.push({ - ...deployBlocker, - isResolved, - }); - } - // Include any existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list - for (const existingPR of currentChecklistData?.PRListMobileExpensify ?? []) { - const isAlreadyIncluded = PRListMobileExpensify.findIndex((pr) => pr.number === existingPR.number) >= 0; - if (!isAlreadyIncluded) { - PRListMobileExpensify.push(existingPR); - } - } - const didVersionChange = newVersion !== currentChecklistData?.version; - const stagingDeployCashBodyAndAssignees = await GithubUtils_1.default.generateStagingDeployCashBodyAndAssignees({ - tag: newVersion, - PRList: PRList.map((pr) => pr.url), - PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.url), - verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.url), - verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.url), - deployBlockers: deployBlockers.map((blocker) => blocker.url), - resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), - resolvedInternalQAPRs: currentChecklistData?.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.url), - isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, - isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, - previousTag: previousChecklistData.version, - chronologicalSection, - }); - if (stagingDeployCashBodyAndAssignees) { - checklistBody = stagingDeployCashBodyAndAssignees.issueBody; - checklistAssignees = stagingDeployCashBodyAndAssignees.issueAssignees.filter(Boolean); - } - } - // Finally, create or update the checklist - const defaultPayload = { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - body: checklistBody, - }; - if (shouldCreateNewDeployChecklist) { - const { data: newChecklist } = await GithubUtils_1.default.octokit.issues.create({ - ...defaultPayload, - title: `Deploy Checklist: New Expensify ${(0, format_1.format)(new Date(), CONST_1.default.DATE_FORMAT_STRING)}`, - labels: [CONST_1.default.LABELS.STAGING_DEPLOY, CONST_1.default.LABELS.LOCK_DEPLOY], - assignees: [CONST_1.default.APPLAUSE_BOT].concat(checklistAssignees), - }); - console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); - return newChecklist; + const token = core.getInput('GITHUB_TOKEN', { required: true }); + process.env.GITHUB_TOKEN = token; + await (0, createOrUpdateStagingDeploy_1.default)(); + } + catch (e) { + if (e instanceof Error) { + core.setFailed(e); + return; } - const { data: updatedChecklist } = await GithubUtils_1.default.octokit.issues.update({ - ...defaultPayload, - // eslint-disable-next-line @typescript-eslint/naming-convention - issue_number: currentChecklistData?.number ?? 0, - }); - console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); - return updatedChecklist; - } - catch (err) { - console.error('An unknown error occurred!', err); - core.setFailed(err); + core.setFailed('An unknown error occurred.'); } } if (require.main === require.cache[eval('__filename')]) { - run(); + main(); } -exports["default"] = run; +exports["default"] = createOrUpdateStagingDeploy_1.default; /***/ }), @@ -12145,9 +11880,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -12226,219 +11959,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12749,8 +12269,191 @@ exports["default"] = GithubUtils; /***/ }), -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { +/***/ 3318: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getStagingDeployCash = getStagingDeployCash; +exports.getStagingDeployCashData = getStagingDeployCashData; +exports.generateStagingDeployCashBodyAndAssignees = generateStagingDeployCashBodyAndAssignees; +exports.parseChecklistSection = parseChecklistSection; +const dedent_1 = __importDefault(__nccwpck_require__(6762)); +const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); +const CONST_1 = __importDefault(__nccwpck_require__(9873)); +const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); +const isEmptyObject_1 = __nccwpck_require__(6497); +/** + * Generic checklist section parser. Extracts a section from the issue body, + * parses checkbox items within it, and returns typed objects sorted by number. + */ +function parseChecklistSection(issueBody, sectionRegex, itemRegex, checkedKey, urlTransform) { + const sectionMatch = issueBody?.match(sectionRegex) ?? null; + if (sectionMatch?.length !== 2) { + return []; + } + const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { + const rawUrl = match[2]; + return { + url: urlTransform ? urlTransform(rawUrl) : rawUrl, + number: Number.parseInt(match[3], 10), + [checkedKey]: match[1] === 'x', + }; + }); + return items.sort((a, b) => a.number - b.number); +} +function getStagingDeployCashPRList(issue) { + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); + if (result.length === 0) { + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + } + return result; +} +function getStagingDeployCashPRListMobileExpensify(issue) { + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); +} +function getStagingDeployCashDeployBlockers(issue) { + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); +} +function getStagingDeployCashInternalQA(issue) { + return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved', (url) => url.split('-').at(0)?.trim() ?? ''); +} +async function getStagingDeployCash() { + const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + labels: CONST_1.default.LABELS.STAGING_DEPLOY, + state: 'open', + }); + if (!data.length) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + if (data.length > 1) { + throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return getStagingDeployCashData(issue); +} +function getStagingDeployCashData(issue) { + try { + const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); + const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); + return { + title: issue.title, + url: issue.url, + number: GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(issue.url), + labels: issue.labels, + PRList: getStagingDeployCashPRList(issue), + PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), + deployBlockers: getStagingDeployCashDeployBlockers(issue), + internalQAPRList: getStagingDeployCashInternalQA(issue), + isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, + isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, + version, + tag: `${version}-staging`, + }; + } + catch (exception) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); + } +} +/** + * Generate the issue body and assignees for a StagingDeployCash checklist. + * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. + */ +async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { + const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; + const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); + const internalQAPRMap = new Map(); + for (const { number, mergerLogin } of mergerResults) { + internalQAPRMap.set(number, mergerLogin); + } + console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); + const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; + console.log('Found the following NO QA PRs:', noQAPRNumbers); + const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); + const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); + const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); + const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); + const internalQAPRNumbers = new Set(internalQAPRMap.keys()); + const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); + const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(a) - GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(b)); + const sections = []; + // Header + let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; + if (sortedPRListMobileExpensify.length > 0) { + header += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; + } + sections.push(header); + // PR list + if (sortedPRList.length > 0) { + const items = sortedPRList + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); + } + // Mobile-Expensify PR list + if (sortedPRListMobileExpensify.length > 0) { + const items = sortedPRListMobileExpensify + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); + return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); + } + // Internal QA PR list + if (internalQAPRMap.size > 0) { + console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); + const items = [...internalQAPRMap.entries()] + .map(([prNumber, merger]) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; + }) + .join('\n'); + sections.push(`**Internal QA:**\n${items}\n`); + } + // Deploy blockers + if (deployBlockers.length > 0) { + const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); + sections.push(`**Deploy Blockers:**\n${items}\n`); + } + // Chronological section + if (chronologicalSection) { + sections.push(chronologicalSection); + } + // Deployer verifications + const check = (checked) => (checked ? 'x' : ' '); + sections.push((0, dedent_1.default)(` + **Deployer verifications:** + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. + `).trimEnd()); + // Footer + sections.push('cc @Expensify/applauseleads\n'); + const issueBody = sections.join('\n'); + const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login) => login !== undefined); + return { issueBody, issueAssignees }; +} + + +/***/ }), + +/***/ 7532: +/***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -12884,6 +12587,389 @@ function getPreviousVersion(currentVersion, level) { } +/***/ }), + +/***/ 4287: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.buildChronologicalSection = buildChronologicalSection; +exports.buildNewChecklistParams = buildNewChecklistParams; +exports.buildUpdateChecklistParams = buildUpdateChecklistParams; +exports.preserveCheckboxState = preserveCheckboxState; +/* eslint-disable @typescript-eslint/naming-convention */ +const core = __importStar(__nccwpck_require__(2186)); +const format_1 = __nccwpck_require__(2464); +const fs_1 = __importDefault(__nccwpck_require__(7147)); +const CONST_1 = __importDefault(__nccwpck_require__(9873)); +const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); +const GitUtils_1 = __importDefault(__nccwpck_require__(1547)); +const StagingDeployUtils_1 = __nccwpck_require__(3318); +/** + * Preserve the checked state (isVerified/isResolved) from a previous checklist when building a new one. + */ +function preserveCheckboxState(items, currentList, checkedKey) { + return items.map((item) => { + const existing = currentList?.find((c) => c.number === item.number); + return { ...item, [checkedKey]: existing ? existing[checkedKey] : false }; + }); +} +async function buildChronologicalSection({ chronologicalPREntries, submoduleUpdates, mergedMobileExpensifyPREntries, }) { + if (chronologicalPREntries.length === 0 && submoduleUpdates.length === 0 && mergedMobileExpensifyPREntries.length === 0) { + return ''; + } + const submoduleRunURLs = new Map(); + const results = await Promise.allSettled(submoduleUpdates.map(async (update) => { + const runURL = await GithubUtils_1.default.getWorkflowRunURLForCommit(update.commit, 'testBuildOnPush.yml'); + return { commit: update.commit, runURL }; + })); + for (const result of results) { + if (result.status === 'fulfilled') { + submoduleRunURLs.set(result.value.commit, result.value.runURL); + } + } + const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); + const mobileExpensifyPRsBySubmodule = new Map(); + const mobileExpensifyPRsPendingSubmoduleUpdate = []; + for (const mobileExpensifyPR of mergedMobileExpensifyPREntries) { + const matchingUpdate = sortedSubmoduleUpdates.find((update) => update.date.localeCompare(mobileExpensifyPR.date) >= 0); + if (matchingUpdate) { + const existing = mobileExpensifyPRsBySubmodule.get(matchingUpdate.commit) ?? []; + existing.push(mobileExpensifyPR); + mobileExpensifyPRsBySubmodule.set(matchingUpdate.commit, existing); + } + else { + mobileExpensifyPRsPendingSubmoduleUpdate.push(mobileExpensifyPR); + } + } + const timeline = [ + ...chronologicalPREntries.map((pr) => ({ type: 'pr', prNumber: pr.prNumber, date: pr.date })), + ...submoduleUpdates.map((update) => ({ type: 'submodule', version: update.version, date: update.date, commit: update.commit })), + ].sort((a, b) => a.date.localeCompare(b.date)); + let section = '
\nChronologically ordered merged PRs (oldest first)\n\n'; + let prIndex = 0; + for (const entry of timeline) { + if (entry.type === 'submodule') { + prIndex++; + const runURL = submoduleRunURLs.get(entry.commit); + const buildLink = runURL ? ` — [Adhoc Build](${runURL})` : ` — ${entry.commit.substring(0, 7)}`; + section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\n`; + const mobileExpensifyPRs = mobileExpensifyPRsBySubmodule.get(entry.commit); + if (mobileExpensifyPRs) { + const sortedMobileExpensifyPRs = [...mobileExpensifyPRs].sort((a, b) => a.date.localeCompare(b.date)); + for (const mobileExpensifyPR of sortedMobileExpensifyPRs) { + const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); + section += ` ↳ ${mobileExpensifyUrl}\n`; + } + } + } + else { + prIndex++; + const url = GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.APP_REPO_URL); + section += `${prIndex}. ${url}\n`; + } + } + if (mobileExpensifyPRsPendingSubmoduleUpdate.length > 0) { + const sortedPending = [...mobileExpensifyPRsPendingSubmoduleUpdate].sort((a, b) => a.date.localeCompare(b.date)); + section += `\n--- PRs waiting for Mobile-Expensify submodule update\n`; + for (const mobileExpensifyPR of sortedPending) { + const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); + section += `${mobileExpensifyUrl}\n`; + } + } + section += '\n
'; + return section; +} +/** + * Build params for creating a brand-new deploy checklist. + */ +function buildNewChecklistParams({ newVersion, newPRNumbers, mergedMobileExpensifyPREntries, previousChecklistData, chronologicalSection, }) { + return { + tag: newVersion, + PRList: newPRNumbers, + PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => pr.prNumber), + previousTag: previousChecklistData.version, + chronologicalSection, + }; +} +/** + * Build params for updating an existing open deploy checklist, preserving verified/resolved state. + */ +async function buildUpdateChecklistParams({ newVersion, newPRNumbers, mergedMobileExpensifyPREntries, previousChecklistData, currentChecklistData, chronologicalSection, }) { + const PRList = preserveCheckboxState(newPRNumbers.map((prNum) => ({ + number: prNum, + url: GithubUtils_1.default.getPullRequestURLFromNumber(prNum, CONST_1.default.APP_REPO_URL), + })), currentChecklistData.PRList, 'isVerified'); + const PRListMobileExpensify = preserveCheckboxState(mergedMobileExpensifyPREntries.map((entry) => ({ + number: entry.prNumber, + url: GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL), + })), currentChecklistData.PRListMobileExpensify, 'isVerified'); + // Include existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list + for (const existingPR of currentChecklistData.PRListMobileExpensify) { + if (!PRListMobileExpensify.some((pr) => pr.number === existingPR.number)) { + PRListMobileExpensify.push(existingPR); + } + } + const openDeployBlockers = await GithubUtils_1.default.paginate(GithubUtils_1.default.octokit.issues.listForRepo, { + log: console, + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + labels: CONST_1.default.LABELS.DEPLOY_BLOCKER, + }); + const deployBlockers = preserveCheckboxState(openDeployBlockers.map((db) => ({ number: db.number, url: db.html_url })), currentChecklistData.deployBlockers, 'isResolved'); + // Add demoted/closed blockers from current checklist and auto-check them + for (const deployBlocker of currentChecklistData.deployBlockers) { + const isResolved = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); + deployBlockers.push({ ...deployBlocker, isResolved }); + } + const didVersionChange = newVersion !== currentChecklistData.version; + return { + tag: newVersion, + PRList: PRList.map((pr) => pr.number), + PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.number), + verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.number), + verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.number), + deployBlockers: deployBlockers.map((blocker) => blocker.url), + resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), + resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.number), + isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, + isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, + previousTag: previousChecklistData.version, + chronologicalSection, + }; +} +async function run() { + // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time + const packageJson = JSON.parse(fs_1.default.readFileSync('package.json', 'utf8')); + const newVersion = packageJson.version; + const newStagingTag = `${packageJson.version}-staging`; + try { + const { data: recentDeployChecklists } = await GithubUtils_1.default.octokit.issues.listForRepo({ + log: console, + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + labels: CONST_1.default.LABELS.STAGING_DEPLOY, + state: 'all', + }); + const mostRecentChecklist = recentDeployChecklists.at(0); + if (!mostRecentChecklist) { + throw new Error('Could not find the most recent checklist'); + } + const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; + const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); + if (shouldCreateNewDeployChecklist) { + console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); + } + else { + console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); + } + if (!previousChecklist) { + throw new Error('Could not find the previous checklist'); + } + const previousChecklistData = (0, StagingDeployUtils_1.getStagingDeployCashData)(previousChecklist); + const currentChecklistData = shouldCreateNewDeployChecklist ? undefined : (0, StagingDeployUtils_1.getStagingDeployCashData)(mostRecentChecklist); + const { mergedPRs: mergedPREntries, submoduleUpdates } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.APP_REPO); + const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); + const previousPRNumbers = new Set(previousChecklistData.PRList.map((pr) => pr.number)); + const previousMobileExpensifyPRNumbers = new Set(previousChecklistData.PRListMobileExpensify.map((pr) => pr.number)); + core.startGroup('Filtering PRs:'); + core.info('mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out'); + core.info(`Found ${previousPRNumbers.size} PRs in the previous checklist:`); + core.info(JSON.stringify(Array.from(previousPRNumbers))); + const newPRNumbers = mergedPRs.filter((prNum) => !previousPRNumbers.has(prNum)); + core.info(`Found ${newPRNumbers.length} PRs deployed since the previous checklist:`); + core.info(JSON.stringify(newPRNumbers)); + const removedPRs = mergedPRs.filter((prNum) => previousPRNumbers.has(prNum)); + if (removedPRs.length > 0) { + core.info(`⚠️⚠️ Filtered out the following cherry-picked PRs that were released with the previous checklist: ${removedPRs.join(', ')} ⚠️⚠️`); + } + core.endGroup(); + console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); + let mergedMobileExpensifyPREntries = []; + try { + const { mergedPRs: allMobileExpensifyPREntries } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.MOBILE_EXPENSIFY_REPO); + mergedMobileExpensifyPREntries = allMobileExpensifyPREntries.filter((pr) => !previousMobileExpensifyPRNumbers.has(pr.prNumber)); + const allCount = allMobileExpensifyPREntries.length; + const newCount = mergedMobileExpensifyPREntries.length; + console.info(`Found ${allCount} total Mobile-Expensify PRs, ${newCount} new ones after filtering:`); + console.info(`Mobile-Expensify PRs: ${mergedMobileExpensifyPREntries.map((pr) => pr.prNumber).join(', ')}`); + const removedMobileExpensifyPRs = allMobileExpensifyPREntries.filter((pr) => previousMobileExpensifyPRNumbers.has(pr.prNumber)); + if (removedMobileExpensifyPRs.length > 0) { + core.info(`⚠️⚠️ Filtered out the following cherry-picked Mobile-Expensify PRs that were released with the previous checklist: ${removedMobileExpensifyPRs.map((pr) => pr.prNumber).join(', ')} ⚠️⚠️`); + } + } + catch (error) { + if (process.env.GITHUB_REPOSITORY !== `${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.APP_REPO}`) { + console.warn("⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessible. This is expected for development/testing on forks."); + } + else { + console.error('Failed to fetch Mobile-Expensify PRs:', error); + } + } + const chronologicalPREntries = mergedPREntries.filter((pr) => !previousPRNumbers.has(pr.prNumber)).sort((a, b) => a.date.localeCompare(b.date)); + const chronologicalSection = await buildChronologicalSection({ + chronologicalPREntries, + submoduleUpdates, + mergedMobileExpensifyPREntries, + }); + let checklistParams; + if (shouldCreateNewDeployChecklist) { + checklistParams = buildNewChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + chronologicalSection, + }); + } + else if (currentChecklistData) { + checklistParams = await buildUpdateChecklistParams({ + newVersion, + newPRNumbers, + mergedMobileExpensifyPREntries, + previousChecklistData, + currentChecklistData, + chronologicalSection, + }); + } + else { + throw new Error('Expected current checklist data for update but it was undefined'); + } + const { issueBody: checklistBody, issueAssignees: checklistAssignees } = await (0, StagingDeployUtils_1.generateStagingDeployCashBodyAndAssignees)(checklistParams); + const defaultPayload = { + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + body: checklistBody, + }; + if (shouldCreateNewDeployChecklist) { + const { data: newChecklist } = await GithubUtils_1.default.octokit.issues.create({ + ...defaultPayload, + title: `Deploy Checklist: New Expensify ${(0, format_1.format)(new Date(), CONST_1.default.DATE_FORMAT_STRING)}`, + labels: [CONST_1.default.LABELS.STAGING_DEPLOY, CONST_1.default.LABELS.LOCK_DEPLOY], + assignees: [CONST_1.default.APPLAUSE_BOT].concat(checklistAssignees), + }); + console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); + return newChecklist; + } + const { data: updatedChecklist } = await GithubUtils_1.default.octokit.issues.update({ + ...defaultPayload, + issue_number: currentChecklistData?.number ?? 0, + }); + console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); + return updatedChecklist; + } + catch (err) { + console.error('An unknown error occurred!', err); + core.setFailed(err); + } +} +if (false) {} +exports["default"] = run; + + +/***/ }), + +/***/ 6762: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +/* + * Note: This file is separated from StringUtils because it is imported by a ts-node script. + * ts-node scripts can't import react-native (because it is written in flow), + * and StringUtils indirectly imports react-native. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports["default"] = dedent; +/** + * Find the minimum indentation of any line in the string, + * and remove that number of leading spaces from every line in the string. + * + * It also removes at most one leading newline, to reflect a common usage: + * + * ``` + * StringUtils.dedent(` + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * `) + * ``` + * + * This implementation assumes you'd want that to be: + * + * ``` + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * + * ``` + * + * Rather than: + * + * ``` + * + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * + * ``` + */ +function dedent(str) { + // Remove at most one leading newline + const stringWithoutLeadingNewlines = str.replaceAll(/^\r?\n/g, ''); + // Split string by remaining newlines + const lines = stringWithoutLeadingNewlines.replaceAll('\r\n', '\n').split('\n'); + // Find the minimum indentation of non-empty lines + let minIndent = Number.MAX_SAFE_INTEGER; + for (const line of lines) { + if (line.trim().length === 0) { + continue; + } + const indentation = line.match(/^ */)?.[0].length ?? 0; + if (indentation < minIndent) { + minIndent = indentation; + } + } + // Remove the common indentation + return lines.map((line) => line.slice(minIndent)).join('\n'); +} + + /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/formatCodeCovComment/index.js b/.github/actions/javascript/formatCodeCovComment/index.js index 7846c4a2a0f9f..2590070839051 100644 --- a/.github/actions/javascript/formatCodeCovComment/index.js +++ b/.github/actions/javascript/formatCodeCovComment/index.js @@ -11916,9 +11916,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11997,219 +11995,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12518,38 +12303,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/getArtifactInfo/index.js b/.github/actions/javascript/getArtifactInfo/index.js index 364b816a3c6d8..c39f3b8c3d425 100644 --- a/.github/actions/javascript/getArtifactInfo/index.js +++ b/.github/actions/javascript/getArtifactInfo/index.js @@ -11690,9 +11690,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11771,219 +11769,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12292,38 +12077,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 82924c5f1dffe..d58e6f1efe1e3 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -12071,9 +12071,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -12152,219 +12150,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12673,38 +12458,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 8982: diff --git a/.github/actions/javascript/getPreviousVersion/index.js b/.github/actions/javascript/getPreviousVersion/index.js index 6d686c5b6458d..1705a91040236 100644 --- a/.github/actions/javascript/getPreviousVersion/index.js +++ b/.github/actions/javascript/getPreviousVersion/index.js @@ -11882,9 +11882,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11963,219 +11961,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12484,38 +12269,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 8982: diff --git a/.github/actions/javascript/getPullRequestDetails/index.js b/.github/actions/javascript/getPullRequestDetails/index.js index 5f0199dfa2c64..4cc192c39c127 100644 --- a/.github/actions/javascript/getPullRequestDetails/index.js +++ b/.github/actions/javascript/getPullRequestDetails/index.js @@ -11819,9 +11819,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11900,219 +11898,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12421,24 +12206,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - /***/ }), /***/ 6497: diff --git a/.github/actions/javascript/getPullRequestIncrementalChanges/index.js b/.github/actions/javascript/getPullRequestIncrementalChanges/index.js index 6dfa914b95fcf..2fde3994de1e3 100644 --- a/.github/actions/javascript/getPullRequestIncrementalChanges/index.js +++ b/.github/actions/javascript/getPullRequestIncrementalChanges/index.js @@ -11921,9 +11921,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -12002,219 +12000,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12523,38 +12308,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 7037: diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isStagingDeployLocked/index.js index 2ec97c92a4202..9838fffca41de 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isStagingDeployLocked/index.js @@ -11532,14 +11532,11 @@ var __importStar = (this && this.__importStar) || (function () { return result; }; })(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(2186)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); +const StagingDeployUtils_1 = __nccwpck_require__(3318); const run = function () { - return GithubUtils_1.default.getStagingDeployCash() + return (0, StagingDeployUtils_1.getStagingDeployCash)() .then(({ labels, number }) => { const labelsNames = labels.map((label) => { if (typeof label === 'string') { @@ -11690,9 +11687,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11771,219 +11766,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12292,6 +12074,189 @@ class GithubUtils { exports["default"] = GithubUtils; +/***/ }), + +/***/ 3318: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getStagingDeployCash = getStagingDeployCash; +exports.getStagingDeployCashData = getStagingDeployCashData; +exports.generateStagingDeployCashBodyAndAssignees = generateStagingDeployCashBodyAndAssignees; +exports.parseChecklistSection = parseChecklistSection; +const dedent_1 = __importDefault(__nccwpck_require__(6762)); +const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); +const CONST_1 = __importDefault(__nccwpck_require__(9873)); +const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); +const isEmptyObject_1 = __nccwpck_require__(6497); +/** + * Generic checklist section parser. Extracts a section from the issue body, + * parses checkbox items within it, and returns typed objects sorted by number. + */ +function parseChecklistSection(issueBody, sectionRegex, itemRegex, checkedKey, urlTransform) { + const sectionMatch = issueBody?.match(sectionRegex) ?? null; + if (sectionMatch?.length !== 2) { + return []; + } + const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { + const rawUrl = match[2]; + return { + url: urlTransform ? urlTransform(rawUrl) : rawUrl, + number: Number.parseInt(match[3], 10), + [checkedKey]: match[1] === 'x', + }; + }); + return items.sort((a, b) => a.number - b.number); +} +function getStagingDeployCashPRList(issue) { + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); + if (result.length === 0) { + console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + } + return result; +} +function getStagingDeployCashPRListMobileExpensify(issue) { + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); +} +function getStagingDeployCashDeployBlockers(issue) { + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); +} +function getStagingDeployCashInternalQA(issue) { + return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved', (url) => url.split('-').at(0)?.trim() ?? ''); +} +async function getStagingDeployCash() { + const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + labels: CONST_1.default.LABELS.STAGING_DEPLOY, + state: 'open', + }); + if (!data.length) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + if (data.length > 1) { + throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return getStagingDeployCashData(issue); +} +function getStagingDeployCashData(issue) { + try { + const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); + const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); + return { + title: issue.title, + url: issue.url, + number: GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(issue.url), + labels: issue.labels, + PRList: getStagingDeployCashPRList(issue), + PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), + deployBlockers: getStagingDeployCashDeployBlockers(issue), + internalQAPRList: getStagingDeployCashInternalQA(issue), + isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, + isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, + version, + tag: `${version}-staging`, + }; + } + catch (exception) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); + } +} +/** + * Generate the issue body and assignees for a StagingDeployCash checklist. + * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. + */ +async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { + const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; + const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); + const internalQAPRMap = new Map(); + for (const { number, mergerLogin } of mergerResults) { + internalQAPRMap.set(number, mergerLogin); + } + console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); + const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; + console.log('Found the following NO QA PRs:', noQAPRNumbers); + const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); + const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); + const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); + const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); + const internalQAPRNumbers = new Set(internalQAPRMap.keys()); + const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); + const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(a) - GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(b)); + const sections = []; + // Header + let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; + if (sortedPRListMobileExpensify.length > 0) { + header += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; + } + sections.push(header); + // PR list + if (sortedPRList.length > 0) { + const items = sortedPRList + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); + } + // Mobile-Expensify PR list + if (sortedPRListMobileExpensify.length > 0) { + const items = sortedPRListMobileExpensify + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); + return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); + } + // Internal QA PR list + if (internalQAPRMap.size > 0) { + console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); + const items = [...internalQAPRMap.entries()] + .map(([prNumber, merger]) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; + }) + .join('\n'); + sections.push(`**Internal QA:**\n${items}\n`); + } + // Deploy blockers + if (deployBlockers.length > 0) { + const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); + sections.push(`**Deploy Blockers:**\n${items}\n`); + } + // Chronological section + if (chronologicalSection) { + sections.push(chronologicalSection); + } + // Deployer verifications + const check = (checked) => (checked ? 'x' : ' '); + sections.push((0, dedent_1.default)(` + **Deployer verifications:** + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. + `).trimEnd()); + // Footer + sections.push('cc @Expensify/applauseleads\n'); + const issueBody = sections.join('\n'); + const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login) => login !== undefined); + return { issueBody, issueAssignees }; +} + + /***/ }), /***/ 7532: @@ -12324,6 +12289,71 @@ function isEmptyObject(obj) { } +/***/ }), + +/***/ 6762: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +/* + * Note: This file is separated from StringUtils because it is imported by a ts-node script. + * ts-node scripts can't import react-native (because it is written in flow), + * and StringUtils indirectly imports react-native. + */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports["default"] = dedent; +/** + * Find the minimum indentation of any line in the string, + * and remove that number of leading spaces from every line in the string. + * + * It also removes at most one leading newline, to reflect a common usage: + * + * ``` + * StringUtils.dedent(` + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * `) + * ``` + * + * This implementation assumes you'd want that to be: + * + * ``` + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * + * ``` + * + * Rather than: + * + * ``` + * + * const myIndentedStr = 'Hello, world!'; + * console.log(myIndentedStr); + * + * ``` + */ +function dedent(str) { + // Remove at most one leading newline + const stringWithoutLeadingNewlines = str.replaceAll(/^\r?\n/g, ''); + // Split string by remaining newlines + const lines = stringWithoutLeadingNewlines.replaceAll('\r\n', '\n').split('\n'); + // Find the minimum indentation of non-empty lines + let minIndent = Number.MAX_SAFE_INTEGER; + for (const line of lines) { + if (line.trim().length === 0) { + continue; + } + const indentation = line.match(/^ */)?.[0].length ?? 0; + if (indentation < minIndent) { + minIndent = indentation; + } + } + // Remove the common indentation + return lines.map((line) => line.slice(minIndent)).join('\n'); +} + + /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index fac4f4614c626..991206f20c2c6 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -13142,9 +13142,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -13223,219 +13221,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -13744,38 +13529,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/postTestBuildComment/index.js b/.github/actions/javascript/postTestBuildComment/index.js index 2545aa9b19fde..11afe79c3d061 100644 --- a/.github/actions/javascript/postTestBuildComment/index.js +++ b/.github/actions/javascript/postTestBuildComment/index.js @@ -11819,9 +11819,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11900,219 +11898,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12421,38 +12206,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/proposalPoliceComment/index.js b/.github/actions/javascript/proposalPoliceComment/index.js index 8398b1decdf3c..70286dfbbb019 100644 --- a/.github/actions/javascript/proposalPoliceComment/index.js +++ b/.github/actions/javascript/proposalPoliceComment/index.js @@ -11966,9 +11966,7 @@ const utils_1 = __nccwpck_require__(73030); const plugin_paginate_rest_1 = __nccwpck_require__(64193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(10537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(17532)); const CONST_1 = __importDefault(__nccwpck_require__(29873)); -const isEmptyObject_1 = __nccwpck_require__(86497); class GithubUtils { static internalOctokit; /** @@ -12047,219 +12045,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12568,38 +12353,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 17532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 86497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 40136: diff --git a/.github/actions/javascript/reopenIssueWithComment/index.js b/.github/actions/javascript/reopenIssueWithComment/index.js index a752f7950b1a8..f972a41e4da31 100644 --- a/.github/actions/javascript/reopenIssueWithComment/index.js +++ b/.github/actions/javascript/reopenIssueWithComment/index.js @@ -11700,9 +11700,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11781,219 +11779,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12302,38 +12087,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/reviewerChecklist/index.js b/.github/actions/javascript/reviewerChecklist/index.js index 0186c8978c2f7..5c5038edcd20b 100644 --- a/.github/actions/javascript/reviewerChecklist/index.js +++ b/.github/actions/javascript/reviewerChecklist/index.js @@ -11792,9 +11792,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11873,219 +11871,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12394,38 +12179,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/verifySignedCommits/index.js b/.github/actions/javascript/verifySignedCommits/index.js index 9fa78e5f14f8f..4fd116fa7d5f1 100644 --- a/.github/actions/javascript/verifySignedCommits/index.js +++ b/.github/actions/javascript/verifySignedCommits/index.js @@ -11732,9 +11732,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11813,219 +11811,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12334,38 +12119,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/.github/actions/javascript/waitForPreviousRuns/index.js b/.github/actions/javascript/waitForPreviousRuns/index.js index ba89631d05c5c..e8b176648b3fc 100644 --- a/.github/actions/javascript/waitForPreviousRuns/index.js +++ b/.github/actions/javascript/waitForPreviousRuns/index.js @@ -11741,9 +11741,7 @@ const utils_1 = __nccwpck_require__(3030); const plugin_paginate_rest_1 = __nccwpck_require__(4193); const plugin_throttling_1 = __nccwpck_require__(9968); const request_error_1 = __nccwpck_require__(537); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const isEmptyObject_1 = __nccwpck_require__(6497); class GithubUtils { static internalOctokit; /** @@ -11822,219 +11820,6 @@ class GithubUtils { // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style return this.internalOctokit.paginate; } - /** - * Finds one open `StagingDeployCash` issue via GitHub octokit library. - */ - static getStagingDeployCash() { - return this.octokit.issues - .listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }) - .then(({ data }) => { - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return this.getStagingDeployCashData(issue); - }); - } - /** - * Takes in a GitHub issue object and returns the data we want. - */ - static getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: this.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: this.getStagingDeployCashPRList(issue), - PRListMobileExpensify: this.getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: this.getStagingDeployCashDeployBlockers(issue), - internalQAPRList: this.getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } - } - /** - * Parse the PRList and Internal QA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashPRList(issue) { - let PRListSection = issue.body?.match(/pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/) ?? null; - if (PRListSection?.length !== 2) { - // No PRs, return an empty array - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - return []; - } - PRListSection = PRListSection[1]; - const PRList = [...PRListSection.matchAll(new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return PRList.sort((a, b) => a.number - b.number); - } - static getStagingDeployCashPRListMobileExpensify(issue) { - let mobileExpensifySection = issue.body?.match(/Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (mobileExpensifySection?.length !== 2) { - return []; - } - mobileExpensifySection = mobileExpensifySection[1]; - const mobileExpensifyPRs = [...mobileExpensifySection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isVerified: match[1] === 'x', - })); - return mobileExpensifyPRs.sort((a, b) => a.number - b.number); - } - /** - * Parse DeployBlocker section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashDeployBlockers(issue) { - let deployBlockerSection = issue.body?.match(/Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/) ?? null; - if (deployBlockerSection?.length !== 2) { - return []; - } - deployBlockerSection = deployBlockerSection[1]; - const deployBlockers = [...deployBlockerSection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2], - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return deployBlockers.sort((a, b) => a.number - b.number); - } - /** - * Parse InternalQA section of the StagingDeployCash issue body. - * - * @private - */ - static getStagingDeployCashInternalQA(issue) { - let internalQASection = issue.body?.match(/Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/) ?? null; - if (internalQASection?.length !== 2) { - return []; - } - internalQASection = internalQASection[1]; - const internalQAPRs = [...internalQASection.matchAll(new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'))].map((match) => ({ - url: match[2].split('-').at(0)?.trim() ?? '', - number: Number.parseInt(match[3], 10), - isResolved: match[1] === 'x', - })); - return internalQAPRs.sort((a, b) => a.number - b.number); - } - /** - * Generate the issue body and assignees for a StagingDeployCash. - */ - static generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - return this.fetchAllPullRequests(PRList.map((pr) => this.getPullRequestNumberFromURL(pr))) - .then((data) => { - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - return Promise.all(internalQAPRs.map((pr) => this.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ url: pr.html_url, mergerLogin })))).then((results) => { - // The format of this map is following: - // { - // 'https://github.com/Expensify/App/pull/9641': 'PauloGasparSv', - // 'https://github.com/Expensify/App/pull/9642': 'mountiny' - // } - const internalQAPRMap = results.reduce((acc, { url, mergerLogin }) => { - acc[url] = mergerLogin; - return acc; - }, {}); - console.log('Found the following Internal QA PRs:', internalQAPRMap); - const noQAPRs = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.html_url) : []; - console.log('Found the following NO QA PRs:', noQAPRs); - const verifiedOrNoQAPRs = new Set([...verifiedPRList, ...verifiedPRListMobileExpensify, ...noQAPRs]); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, Object.keys(internalQAPRMap)))].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => GithubUtils.getPullRequestNumberFromURL(a) - GithubUtils.getPullRequestNumberFromURL(b)); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); - // Tag version and comparison URL - // eslint-disable-next-line max-len - let issueBody = `**Release Version:** \`${tag}\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n`; - // Add Mobile-Expensify compare link if there are Mobile-Expensify PRs - if (sortedPRListMobileExpensify.length > 0) { - issueBody += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\r\n`; - } - issueBody += '\r\n'; - // PR list - if (sortedPRList.length > 0) { - issueBody += '**This release contains changes from the following pull requests:**\r\n'; - for (const URL of sortedPRList) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - issueBody += '**Mobile-Expensify PRs:**\r\n'; - for (const URL of sortedPRListMobileExpensify) { - issueBody += verifiedOrNoQAPRs.has(URL) ? '- [x]' : '- [ ]'; - issueBody += ` ${URL}\r\n`; - } - issueBody += '\r\n\r\n'; - } - // Internal QA PR list - if (!(0, isEmptyObject_1.isEmptyObject)(internalQAPRMap)) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - issueBody += '**Internal QA:**\r\n'; - for (const URL of Object.keys(internalQAPRMap)) { - const merger = internalQAPRMap[URL]; - const mergerMention = `@${merger}`; - issueBody += `${resolvedInternalQAPRs.includes(URL) ? '- [x]' : '- [ ]'} `; - issueBody += `${URL}`; - issueBody += ` - ${mergerMention}`; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - // Deploy blockers - if (deployBlockers.length > 0) { - issueBody += '**Deploy Blockers:**\r\n'; - for (const URL of sortedDeployBlockers) { - issueBody += resolvedDeployBlockers.includes(URL) ? '- [x] ' : '- [ ] '; - issueBody += URL; - issueBody += '\r\n'; - } - issueBody += '\r\n\r\n'; - } - if (chronologicalSection) { - issueBody += chronologicalSection; - issueBody += '\r\n\r\n'; - } - issueBody += '**Deployer verifications:**'; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isSentryChecked ? 'x' : ' '}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096).`; - // eslint-disable-next-line max-len - issueBody += `\r\n- [${isGHStatusChecked ? 'x' : ' '}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.`; - issueBody += '\r\n\r\ncc @Expensify/applauseleads\r\n'; - const issueAssignees = [...new Set(Object.values(internalQAPRMap))]; - const issue = { issueBody, issueAssignees }; - return issue; - }); - }) - .catch((err) => console.warn('Error generating StagingDeployCash issue body! Continuing...', err)); - } /** * Fetch all pull requests given a list of PR numbers. */ @@ -12343,38 +12128,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 9491: diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateStagingDeploy.ts index 8c51fe0ddc34f..42a425cc0cf7b 100644 --- a/scripts/createOrUpdateStagingDeploy.ts +++ b/scripts/createOrUpdateStagingDeploy.ts @@ -290,7 +290,7 @@ async function run(): Promise { } catch (error) { if (process.env.GITHUB_REPOSITORY !== `${CONST.GITHUB_OWNER}/${CONST.APP_REPO}`) { console.warn( - "⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessble. This is expected for development/testing on forks.", + "⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessible. This is expected for development/testing on forks.", ); } else { console.error('Failed to fetch Mobile-Expensify PRs:', error); From 79bc7f5acf66c0a90ad282904d3993dd4569d98a Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 20 Feb 2026 17:20:33 -0800 Subject: [PATCH 3/8] Remove action wrapper and run script directly via ts-node The thin action wrapper only read GITHUB_TOKEN from action input and delegated to the core script. Running the script directly with `npx ts-node` eliminates the ncc build step for this action and reduces file count. Co-authored-by: Cursor --- .../createOrUpdateStagingDeploy/action.yml | 10 - .../createOrUpdateStagingDeploy.ts | 22 - .../createOrUpdateStagingDeploy/index.js | 16647 ---------------- .github/scripts/buildActions.sh | 2 +- .github/workflows/createDeployChecklist.yml | 4 +- 5 files changed, 3 insertions(+), 16682 deletions(-) delete mode 100644 .github/actions/javascript/createOrUpdateStagingDeploy/action.yml delete mode 100644 .github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts delete mode 100644 .github/actions/javascript/createOrUpdateStagingDeploy/index.js diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml b/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml deleted file mode 100644 index b228c694566c3..0000000000000 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: 'Create or Update StagingDeployCash' -description: 'Creates a new StagingDeployCash issue if there is not one open, or updates the existing one.' -inputs: - GITHUB_TOKEN: - description: Auth token for New Expensify Github - required: true - -runs: - using: 'node20' - main: './index.js' diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts b/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts deleted file mode 100644 index fa6bbff44858d..0000000000000 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as core from '@actions/core'; -import run from '@scripts/createOrUpdateStagingDeploy'; - -async function main() { - try { - const token = core.getInput('GITHUB_TOKEN', {required: true}); - process.env.GITHUB_TOKEN = token; - await run(); - } catch (e) { - if (e instanceof Error) { - core.setFailed(e); - return; - } - core.setFailed('An unknown error occurred.'); - } -} - -if (require.main === module) { - main(); -} - -export default run; diff --git a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js b/.github/actions/javascript/createOrUpdateStagingDeploy/index.js deleted file mode 100644 index 272a9fa172d7c..0000000000000 --- a/.github/actions/javascript/createOrUpdateStagingDeploy/index.js +++ /dev/null @@ -1,16647 +0,0 @@ -/** - * NOTE: This is a compiled file. DO NOT directly edit this file. - */ -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 7351: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.issue = exports.issueCommand = void 0; -const os = __importStar(__nccwpck_require__(2037)); -const utils_1 = __nccwpck_require__(5278); -/** - * Commands - * - * Command Format: - * ::name key=value,key=value::message - * - * Examples: - * ::warning::This is the message - * ::set-env name=MY_VAR::some value - */ -function issueCommand(command, properties, message) { - const cmd = new Command(command, properties, message); - process.stdout.write(cmd.toString() + os.EOL); -} -exports.issueCommand = issueCommand; -function issue(name, message = '') { - issueCommand(name, {}, message); -} -exports.issue = issue; -const CMD_STRING = '::'; -class Command { - constructor(command, properties, message) { - if (!command) { - command = 'missing.command'; - } - this.command = command; - this.properties = properties; - this.message = message; - } - toString() { - let cmdStr = CMD_STRING + this.command; - if (this.properties && Object.keys(this.properties).length > 0) { - cmdStr += ' '; - let first = true; - for (const key in this.properties) { - if (this.properties.hasOwnProperty(key)) { - const val = this.properties[key]; - if (val) { - if (first) { - first = false; - } - else { - cmdStr += ','; - } - cmdStr += `${key}=${escapeProperty(val)}`; - } - } - } - } - cmdStr += `${CMD_STRING}${escapeData(this.message)}`; - return cmdStr; - } -} -function escapeData(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A'); -} -function escapeProperty(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A') - .replace(/:/g, '%3A') - .replace(/,/g, '%2C'); -} -//# sourceMappingURL=command.js.map - -/***/ }), - -/***/ 2186: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; -const command_1 = __nccwpck_require__(7351); -const file_command_1 = __nccwpck_require__(717); -const utils_1 = __nccwpck_require__(5278); -const os = __importStar(__nccwpck_require__(2037)); -const path = __importStar(__nccwpck_require__(1017)); -const oidc_utils_1 = __nccwpck_require__(8041); -/** - * The code to exit an action - */ -var ExitCode; -(function (ExitCode) { - /** - * A code indicating that the action was successful - */ - ExitCode[ExitCode["Success"] = 0] = "Success"; - /** - * A code indicating that the action was a failure - */ - ExitCode[ExitCode["Failure"] = 1] = "Failure"; -})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); -//----------------------------------------------------------------------- -// Variables -//----------------------------------------------------------------------- -/** - * Sets env variable for this action and future actions in the job - * @param name the name of the variable to set - * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function exportVariable(name, val) { - const convertedVal = utils_1.toCommandValue(val); - process.env[name] = convertedVal; - const filePath = process.env['GITHUB_ENV'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); - } - command_1.issueCommand('set-env', { name }, convertedVal); -} -exports.exportVariable = exportVariable; -/** - * Registers a secret which will get masked from logs - * @param secret value of the secret - */ -function setSecret(secret) { - command_1.issueCommand('add-mask', {}, secret); -} -exports.setSecret = setSecret; -/** - * Prepends inputPath to the PATH (for this action and future actions) - * @param inputPath - */ -function addPath(inputPath) { - const filePath = process.env['GITHUB_PATH'] || ''; - if (filePath) { - file_command_1.issueFileCommand('PATH', inputPath); - } - else { - command_1.issueCommand('add-path', {}, inputPath); - } - process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; -} -exports.addPath = addPath; -/** - * Gets the value of an input. - * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. - * Returns an empty string if the value is not defined. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string - */ -function getInput(name, options) { - const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; - if (options && options.required && !val) { - throw new Error(`Input required and not supplied: ${name}`); - } - if (options && options.trimWhitespace === false) { - return val; - } - return val.trim(); -} -exports.getInput = getInput; -/** - * Gets the values of an multiline input. Each value is also trimmed. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string[] - * - */ -function getMultilineInput(name, options) { - const inputs = getInput(name, options) - .split('\n') - .filter(x => x !== ''); - if (options && options.trimWhitespace === false) { - return inputs; - } - return inputs.map(input => input.trim()); -} -exports.getMultilineInput = getMultilineInput; -/** - * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. - * Support boolean input list: `true | True | TRUE | false | False | FALSE` . - * The return value is also in boolean type. - * ref: https://yaml.org/spec/1.2/spec.html#id2804923 - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns boolean - */ -function getBooleanInput(name, options) { - const trueValue = ['true', 'True', 'TRUE']; - const falseValue = ['false', 'False', 'FALSE']; - const val = getInput(name, options); - if (trueValue.includes(val)) - return true; - if (falseValue.includes(val)) - return false; - throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + - `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); -} -exports.getBooleanInput = getBooleanInput; -/** - * Sets the value of an output. - * - * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setOutput(name, value) { - const filePath = process.env['GITHUB_OUTPUT'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); - } - process.stdout.write(os.EOL); - command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); -} -exports.setOutput = setOutput; -/** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. - * - */ -function setCommandEcho(enabled) { - command_1.issue('echo', enabled ? 'on' : 'off'); -} -exports.setCommandEcho = setCommandEcho; -//----------------------------------------------------------------------- -// Results -//----------------------------------------------------------------------- -/** - * Sets the action status to failed. - * When the action exits it will be with an exit code of 1 - * @param message add error issue message - */ -function setFailed(message) { - process.exitCode = ExitCode.Failure; - error(message); -} -exports.setFailed = setFailed; -//----------------------------------------------------------------------- -// Logging Commands -//----------------------------------------------------------------------- -/** - * Gets whether Actions Step Debug is on or not - */ -function isDebug() { - return process.env['RUNNER_DEBUG'] === '1'; -} -exports.isDebug = isDebug; -/** - * Writes debug message to user log - * @param message debug message - */ -function debug(message) { - command_1.issueCommand('debug', {}, message); -} -exports.debug = debug; -/** - * Adds an error issue - * @param message error issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function error(message, properties = {}) { - command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.error = error; -/** - * Adds a warning issue - * @param message warning issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function warning(message, properties = {}) { - command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.warning = warning; -/** - * Adds a notice issue - * @param message notice issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function notice(message, properties = {}) { - command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.notice = notice; -/** - * Writes info to log with console.log. - * @param message info message - */ -function info(message) { - process.stdout.write(message + os.EOL); -} -exports.info = info; -/** - * Begin an output group. - * - * Output until the next `groupEnd` will be foldable in this group - * - * @param name The name of the output group - */ -function startGroup(name) { - command_1.issue('group', name); -} -exports.startGroup = startGroup; -/** - * End an output group. - */ -function endGroup() { - command_1.issue('endgroup'); -} -exports.endGroup = endGroup; -/** - * Wrap an asynchronous function call in a group. - * - * Returns the same type as the function itself. - * - * @param name The name of the group - * @param fn The function to wrap in the group - */ -function group(name, fn) { - return __awaiter(this, void 0, void 0, function* () { - startGroup(name); - let result; - try { - result = yield fn(); - } - finally { - endGroup(); - } - return result; - }); -} -exports.group = group; -//----------------------------------------------------------------------- -// Wrapper action state -//----------------------------------------------------------------------- -/** - * Saves state for current action, the state can only be retrieved by this action's post job execution. - * - * @param name name of the state to store - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function saveState(name, value) { - const filePath = process.env['GITHUB_STATE'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); - } - command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); -} -exports.saveState = saveState; -/** - * Gets the value of an state set by this action's main execution. - * - * @param name name of the state to get - * @returns string - */ -function getState(name) { - return process.env[`STATE_${name}`] || ''; -} -exports.getState = getState; -function getIDToken(aud) { - return __awaiter(this, void 0, void 0, function* () { - return yield oidc_utils_1.OidcClient.getIDToken(aud); - }); -} -exports.getIDToken = getIDToken; -/** - * Summary exports - */ -var summary_1 = __nccwpck_require__(1327); -Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); -/** - * @deprecated use core.summary - */ -var summary_2 = __nccwpck_require__(1327); -Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); -/** - * Path exports - */ -var path_utils_1 = __nccwpck_require__(2981); -Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); -Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); -Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); -//# sourceMappingURL=core.js.map - -/***/ }), - -/***/ 717: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -// For internal use, subject to change. -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -const fs = __importStar(__nccwpck_require__(7147)); -const os = __importStar(__nccwpck_require__(2037)); -const uuid_1 = __nccwpck_require__(5840); -const utils_1 = __nccwpck_require__(5278); -function issueFileCommand(command, message) { - const filePath = process.env[`GITHUB_${command}`]; - if (!filePath) { - throw new Error(`Unable to find environment variable for file command ${command}`); - } - if (!fs.existsSync(filePath)) { - throw new Error(`Missing file at path: ${filePath}`); - } - fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { - encoding: 'utf8' - }); -} -exports.issueFileCommand = issueFileCommand; -function prepareKeyValueMessage(key, value) { - const delimiter = `ghadelimiter_${uuid_1.v4()}`; - const convertedValue = utils_1.toCommandValue(value); - // These should realistically never happen, but just in case someone finds a - // way to exploit uuid generation let's not allow keys or values that contain - // the delimiter. - if (key.includes(delimiter)) { - throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); - } - if (convertedValue.includes(delimiter)) { - throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); - } - return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; -} -exports.prepareKeyValueMessage = prepareKeyValueMessage; -//# sourceMappingURL=file-command.js.map - -/***/ }), - -/***/ 8041: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.OidcClient = void 0; -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const core_1 = __nccwpck_require__(2186); -class OidcClient { - static createHttpClient(allowRetry = true, maxRetry = 10) { - const requestOptions = { - allowRetries: allowRetry, - maxRetries: maxRetry - }; - return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); - } - static getRequestToken() { - const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; - if (!token) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); - } - return token; - } - static getIDTokenUrl() { - const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; - if (!runtimeUrl) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); - } - return runtimeUrl; - } - static getCall(id_token_url) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const httpclient = OidcClient.createHttpClient(); - const res = yield httpclient - .getJson(id_token_url) - .catch(error => { - throw new Error(`Failed to get ID Token. \n - Error Code : ${error.statusCode}\n - Error Message: ${error.result.message}`); - }); - const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; - if (!id_token) { - throw new Error('Response json body do not have ID Token field'); - } - return id_token; - }); - } - static getIDToken(audience) { - return __awaiter(this, void 0, void 0, function* () { - try { - // New ID Token is requested from action service - let id_token_url = OidcClient.getIDTokenUrl(); - if (audience) { - const encodedAudience = encodeURIComponent(audience); - id_token_url = `${id_token_url}&audience=${encodedAudience}`; - } - core_1.debug(`ID token url is ${id_token_url}`); - const id_token = yield OidcClient.getCall(id_token_url); - core_1.setSecret(id_token); - return id_token; - } - catch (error) { - throw new Error(`Error message: ${error.message}`); - } - }); - } -} -exports.OidcClient = OidcClient; -//# sourceMappingURL=oidc-utils.js.map - -/***/ }), - -/***/ 2981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; -const path = __importStar(__nccwpck_require__(1017)); -/** - * toPosixPath converts the given path to the posix form. On Windows, \\ will be - * replaced with /. - * - * @param pth. Path to transform. - * @return string Posix path. - */ -function toPosixPath(pth) { - return pth.replace(/[\\]/g, '/'); -} -exports.toPosixPath = toPosixPath; -/** - * toWin32Path converts the given path to the win32 form. On Linux, / will be - * replaced with \\. - * - * @param pth. Path to transform. - * @return string Win32 path. - */ -function toWin32Path(pth) { - return pth.replace(/[/]/g, '\\'); -} -exports.toWin32Path = toWin32Path; -/** - * toPlatformPath converts the given path to a platform-specific path. It does - * this by replacing instances of / and \ with the platform-specific path - * separator. - * - * @param pth The path to platformize. - * @return string The platform-specific path. - */ -function toPlatformPath(pth) { - return pth.replace(/[/\\]/g, path.sep); -} -exports.toPlatformPath = toPlatformPath; -//# sourceMappingURL=path-utils.js.map - -/***/ }), - -/***/ 1327: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; -const os_1 = __nccwpck_require__(2037); -const fs_1 = __nccwpck_require__(7147); -const { access, appendFile, writeFile } = fs_1.promises; -exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; -exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; -class Summary { - constructor() { - this._buffer = ''; - } - /** - * Finds the summary file path from the environment, rejects if env var is not found or file does not exist - * Also checks r/w permissions. - * - * @returns step summary file path - */ - filePath() { - return __awaiter(this, void 0, void 0, function* () { - if (this._filePath) { - return this._filePath; - } - const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; - if (!pathFromEnv) { - throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); - } - try { - yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); - } - catch (_a) { - throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); - } - this._filePath = pathFromEnv; - return this._filePath; - }); - } - /** - * Wraps content in an HTML tag, adding any HTML attributes - * - * @param {string} tag HTML tag to wrap - * @param {string | null} content content within the tag - * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add - * - * @returns {string} content wrapped in HTML element - */ - wrap(tag, content, attrs = {}) { - const htmlAttrs = Object.entries(attrs) - .map(([key, value]) => ` ${key}="${value}"`) - .join(''); - if (!content) { - return `<${tag}${htmlAttrs}>`; - } - return `<${tag}${htmlAttrs}>${content}`; - } - /** - * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. - * - * @param {SummaryWriteOptions} [options] (optional) options for write operation - * - * @returns {Promise} summary instance - */ - write(options) { - return __awaiter(this, void 0, void 0, function* () { - const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); - const filePath = yield this.filePath(); - const writeFunc = overwrite ? writeFile : appendFile; - yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); - return this.emptyBuffer(); - }); - } - /** - * Clears the summary buffer and wipes the summary file - * - * @returns {Summary} summary instance - */ - clear() { - return __awaiter(this, void 0, void 0, function* () { - return this.emptyBuffer().write({ overwrite: true }); - }); - } - /** - * Returns the current summary buffer as a string - * - * @returns {string} string of summary buffer - */ - stringify() { - return this._buffer; - } - /** - * If the summary buffer is empty - * - * @returns {boolen} true if the buffer is empty - */ - isEmptyBuffer() { - return this._buffer.length === 0; - } - /** - * Resets the summary buffer without writing to summary file - * - * @returns {Summary} summary instance - */ - emptyBuffer() { - this._buffer = ''; - return this; - } - /** - * Adds raw text to the summary buffer - * - * @param {string} text content to add - * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) - * - * @returns {Summary} summary instance - */ - addRaw(text, addEOL = false) { - this._buffer += text; - return addEOL ? this.addEOL() : this; - } - /** - * Adds the operating system-specific end-of-line marker to the buffer - * - * @returns {Summary} summary instance - */ - addEOL() { - return this.addRaw(os_1.EOL); - } - /** - * Adds an HTML codeblock to the summary buffer - * - * @param {string} code content to render within fenced code block - * @param {string} lang (optional) language to syntax highlight code - * - * @returns {Summary} summary instance - */ - addCodeBlock(code, lang) { - const attrs = Object.assign({}, (lang && { lang })); - const element = this.wrap('pre', this.wrap('code', code), attrs); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML list to the summary buffer - * - * @param {string[]} items list of items to render - * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) - * - * @returns {Summary} summary instance - */ - addList(items, ordered = false) { - const tag = ordered ? 'ol' : 'ul'; - const listItems = items.map(item => this.wrap('li', item)).join(''); - const element = this.wrap(tag, listItems); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML table to the summary buffer - * - * @param {SummaryTableCell[]} rows table rows - * - * @returns {Summary} summary instance - */ - addTable(rows) { - const tableBody = rows - .map(row => { - const cells = row - .map(cell => { - if (typeof cell === 'string') { - return this.wrap('td', cell); - } - const { header, data, colspan, rowspan } = cell; - const tag = header ? 'th' : 'td'; - const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); - return this.wrap(tag, data, attrs); - }) - .join(''); - return this.wrap('tr', cells); - }) - .join(''); - const element = this.wrap('table', tableBody); - return this.addRaw(element).addEOL(); - } - /** - * Adds a collapsable HTML details element to the summary buffer - * - * @param {string} label text for the closed state - * @param {string} content collapsable content - * - * @returns {Summary} summary instance - */ - addDetails(label, content) { - const element = this.wrap('details', this.wrap('summary', label) + content); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML image tag to the summary buffer - * - * @param {string} src path to the image you to embed - * @param {string} alt text description of the image - * @param {SummaryImageOptions} options (optional) addition image attributes - * - * @returns {Summary} summary instance - */ - addImage(src, alt, options) { - const { width, height } = options || {}; - const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); - const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML section heading element - * - * @param {string} text heading text - * @param {number | string} [level=1] (optional) the heading level, default: 1 - * - * @returns {Summary} summary instance - */ - addHeading(text, level) { - const tag = `h${level}`; - const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) - ? tag - : 'h1'; - const element = this.wrap(allowedTag, text); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML thematic break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addSeparator() { - const element = this.wrap('hr', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML line break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addBreak() { - const element = this.wrap('br', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML blockquote to the summary buffer - * - * @param {string} text quote text - * @param {string} cite (optional) citation url - * - * @returns {Summary} summary instance - */ - addQuote(text, cite) { - const attrs = Object.assign({}, (cite && { cite })); - const element = this.wrap('blockquote', text, attrs); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML anchor tag to the summary buffer - * - * @param {string} text link text/content - * @param {string} href hyperlink - * - * @returns {Summary} summary instance - */ - addLink(text, href) { - const element = this.wrap('a', text, { href }); - return this.addRaw(element).addEOL(); - } -} -const _summary = new Summary(); -/** - * @deprecated use `core.summary` - */ -exports.markdownSummary = _summary; -exports.summary = _summary; -//# sourceMappingURL=summary.js.map - -/***/ }), - -/***/ 5278: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toCommandProperties = exports.toCommandValue = void 0; -/** - * Sanitizes an input into a string so it can be passed into issueCommand safely - * @param input input to sanitize into a string - */ -function toCommandValue(input) { - if (input === null || input === undefined) { - return ''; - } - else if (typeof input === 'string' || input instanceof String) { - return input; - } - return JSON.stringify(input); -} -exports.toCommandValue = toCommandValue; -/** - * - * @param annotationProperties - * @returns The command properties to send with the actual annotation command - * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 - */ -function toCommandProperties(annotationProperties) { - if (!Object.keys(annotationProperties).length) { - return {}; - } - return { - title: annotationProperties.title, - file: annotationProperties.file, - line: annotationProperties.startLine, - endLine: annotationProperties.endLine, - col: annotationProperties.startColumn, - endColumn: annotationProperties.endColumn - }; -} -exports.toCommandProperties = toCommandProperties; -//# sourceMappingURL=utils.js.map - -/***/ }), - -/***/ 4087: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.Context = void 0; -const fs_1 = __nccwpck_require__(7147); -const os_1 = __nccwpck_require__(2037); -class Context { - /** - * Hydrate the context from the environment - */ - constructor() { - var _a, _b, _c; - this.payload = {}; - if (process.env.GITHUB_EVENT_PATH) { - if (fs_1.existsSync(process.env.GITHUB_EVENT_PATH)) { - this.payload = JSON.parse(fs_1.readFileSync(process.env.GITHUB_EVENT_PATH, { encoding: 'utf8' })); - } - else { - const path = process.env.GITHUB_EVENT_PATH; - process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${os_1.EOL}`); - } - } - this.eventName = process.env.GITHUB_EVENT_NAME; - this.sha = process.env.GITHUB_SHA; - this.ref = process.env.GITHUB_REF; - this.workflow = process.env.GITHUB_WORKFLOW; - this.action = process.env.GITHUB_ACTION; - this.actor = process.env.GITHUB_ACTOR; - this.job = process.env.GITHUB_JOB; - this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER, 10); - this.runId = parseInt(process.env.GITHUB_RUN_ID, 10); - this.apiUrl = (_a = process.env.GITHUB_API_URL) !== null && _a !== void 0 ? _a : `https://api.github.com`; - this.serverUrl = (_b = process.env.GITHUB_SERVER_URL) !== null && _b !== void 0 ? _b : `https://github.com`; - this.graphqlUrl = (_c = process.env.GITHUB_GRAPHQL_URL) !== null && _c !== void 0 ? _c : `https://api.github.com/graphql`; - } - get issue() { - const payload = this.payload; - return Object.assign(Object.assign({}, this.repo), { number: (payload.issue || payload.pull_request || payload).number }); - } - get repo() { - if (process.env.GITHUB_REPOSITORY) { - const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); - return { owner, repo }; - } - if (this.payload.repository) { - return { - owner: this.payload.repository.owner.login, - repo: this.payload.repository.name - }; - } - throw new Error("context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"); - } -} -exports.Context = Context; -//# sourceMappingURL=context.js.map - -/***/ }), - -/***/ 7914: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getApiBaseUrl = exports.getProxyAgent = exports.getAuthString = void 0; -const httpClient = __importStar(__nccwpck_require__(6255)); -function getAuthString(token, options) { - if (!token && !options.auth) { - throw new Error('Parameter token or opts.auth is required'); - } - else if (token && options.auth) { - throw new Error('Parameters token and opts.auth may not both be specified'); - } - return typeof options.auth === 'string' ? options.auth : `token ${token}`; -} -exports.getAuthString = getAuthString; -function getProxyAgent(destinationUrl) { - const hc = new httpClient.HttpClient(); - return hc.getAgent(destinationUrl); -} -exports.getProxyAgent = getProxyAgent; -function getApiBaseUrl() { - return process.env['GITHUB_API_URL'] || 'https://api.github.com'; -} -exports.getApiBaseUrl = getApiBaseUrl; -//# sourceMappingURL=utils.js.map - -/***/ }), - -/***/ 3030: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getOctokitOptions = exports.GitHub = exports.defaults = exports.context = void 0; -const Context = __importStar(__nccwpck_require__(4087)); -const Utils = __importStar(__nccwpck_require__(7914)); -// octokit + plugins -const core_1 = __nccwpck_require__(8525); -const plugin_rest_endpoint_methods_1 = __nccwpck_require__(3044); -const plugin_paginate_rest_1 = __nccwpck_require__(8945); -exports.context = new Context.Context(); -const baseUrl = Utils.getApiBaseUrl(); -exports.defaults = { - baseUrl, - request: { - agent: Utils.getProxyAgent(baseUrl) - } -}; -exports.GitHub = core_1.Octokit.plugin(plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_paginate_rest_1.paginateRest).defaults(exports.defaults); -/** - * Convience function to correctly format Octokit Options to pass into the constructor. - * - * @param token the repo PAT or GITHUB_TOKEN - * @param options other options to set - */ -function getOctokitOptions(token, options) { - const opts = Object.assign({}, options || {}); // Shallow clone - don't mutate the object provided by the caller - // Auth - const auth = Utils.getAuthString(token, opts); - if (auth) { - opts.auth = auth; - } - return opts; -} -exports.getOctokitOptions = getOctokitOptions; -//# sourceMappingURL=utils.js.map - -/***/ }), - -/***/ 673: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -const REGEX_IS_INSTALLATION_LEGACY = /^v1\./; -const REGEX_IS_INSTALLATION = /^ghs_/; -const REGEX_IS_USER_TO_SERVER = /^ghu_/; -async function auth(token) { - const isApp = token.split(/\./).length === 3; - const isInstallation = REGEX_IS_INSTALLATION_LEGACY.test(token) || REGEX_IS_INSTALLATION.test(token); - const isUserToServer = REGEX_IS_USER_TO_SERVER.test(token); - const tokenType = isApp ? "app" : isInstallation ? "installation" : isUserToServer ? "user-to-server" : "oauth"; - return { - type: "token", - token: token, - tokenType - }; -} - -/** - * Prefix token for usage in the Authorization header - * - * @param token OAuth token or JSON Web Token - */ -function withAuthorizationPrefix(token) { - if (token.split(/\./).length === 3) { - return `bearer ${token}`; - } - - return `token ${token}`; -} - -async function hook(token, request, route, parameters) { - const endpoint = request.endpoint.merge(route, parameters); - endpoint.headers.authorization = withAuthorizationPrefix(token); - return request(endpoint); -} - -const createTokenAuth = function createTokenAuth(token) { - if (!token) { - throw new Error("[@octokit/auth-token] No token passed to createTokenAuth"); - } - - if (typeof token !== "string") { - throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string"); - } - - token = token.replace(/^(token|bearer) +/i, ""); - return Object.assign(auth.bind(null, token), { - hook: hook.bind(null, token) - }); -}; - -exports.createTokenAuth = createTokenAuth; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 8525: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -var universalUserAgent = __nccwpck_require__(5030); -var beforeAfterHook = __nccwpck_require__(3682); -var request = __nccwpck_require__(9353); -var graphql = __nccwpck_require__(6422); -var authToken = __nccwpck_require__(673); - -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - - return target; -} - -function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - - var target = _objectWithoutPropertiesLoose(source, excluded); - - var key, i; - - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - - return target; -} - -const VERSION = "3.6.0"; - -const _excluded = ["authStrategy"]; -class Octokit { - constructor(options = {}) { - const hook = new beforeAfterHook.Collection(); - const requestDefaults = { - baseUrl: request.request.endpoint.DEFAULTS.baseUrl, - headers: {}, - request: Object.assign({}, options.request, { - // @ts-ignore internal usage only, no need to type - hook: hook.bind(null, "request") - }), - mediaType: { - previews: [], - format: "" - } - }; // prepend default user agent with `options.userAgent` if set - - requestDefaults.headers["user-agent"] = [options.userAgent, `octokit-core.js/${VERSION} ${universalUserAgent.getUserAgent()}`].filter(Boolean).join(" "); - - if (options.baseUrl) { - requestDefaults.baseUrl = options.baseUrl; - } - - if (options.previews) { - requestDefaults.mediaType.previews = options.previews; - } - - if (options.timeZone) { - requestDefaults.headers["time-zone"] = options.timeZone; - } - - this.request = request.request.defaults(requestDefaults); - this.graphql = graphql.withCustomRequest(this.request).defaults(requestDefaults); - this.log = Object.assign({ - debug: () => {}, - info: () => {}, - warn: console.warn.bind(console), - error: console.error.bind(console) - }, options.log); - this.hook = hook; // (1) If neither `options.authStrategy` nor `options.auth` are set, the `octokit` instance - // is unauthenticated. The `this.auth()` method is a no-op and no request hook is registered. - // (2) If only `options.auth` is set, use the default token authentication strategy. - // (3) If `options.authStrategy` is set then use it and pass in `options.auth`. Always pass own request as many strategies accept a custom request instance. - // TODO: type `options.auth` based on `options.authStrategy`. - - if (!options.authStrategy) { - if (!options.auth) { - // (1) - this.auth = async () => ({ - type: "unauthenticated" - }); - } else { - // (2) - const auth = authToken.createTokenAuth(options.auth); // @ts-ignore ¯\_(ツ)_/¯ - - hook.wrap("request", auth.hook); - this.auth = auth; - } - } else { - const { - authStrategy - } = options, - otherOptions = _objectWithoutProperties(options, _excluded); - - const auth = authStrategy(Object.assign({ - request: this.request, - log: this.log, - // we pass the current octokit instance as well as its constructor options - // to allow for authentication strategies that return a new octokit instance - // that shares the same internal state as the current one. The original - // requirement for this was the "event-octokit" authentication strategy - // of https://github.com/probot/octokit-auth-probot. - octokit: this, - octokitOptions: otherOptions - }, options.auth)); // @ts-ignore ¯\_(ツ)_/¯ - - hook.wrap("request", auth.hook); - this.auth = auth; - } // apply plugins - // https://stackoverflow.com/a/16345172 - - - const classConstructor = this.constructor; - classConstructor.plugins.forEach(plugin => { - Object.assign(this, plugin(this, options)); - }); - } - - static defaults(defaults) { - const OctokitWithDefaults = class extends this { - constructor(...args) { - const options = args[0] || {}; - - if (typeof defaults === "function") { - super(defaults(options)); - return; - } - - super(Object.assign({}, defaults, options, options.userAgent && defaults.userAgent ? { - userAgent: `${options.userAgent} ${defaults.userAgent}` - } : null)); - } - - }; - return OctokitWithDefaults; - } - /** - * Attach a plugin (or many) to your Octokit instance. - * - * @example - * const API = Octokit.plugin(plugin1, plugin2, plugin3, ...) - */ - - - static plugin(...newPlugins) { - var _a; - - const currentPlugins = this.plugins; - const NewOctokit = (_a = class extends this {}, _a.plugins = currentPlugins.concat(newPlugins.filter(plugin => !currentPlugins.includes(plugin))), _a); - return NewOctokit; - } - -} -Octokit.VERSION = VERSION; -Octokit.plugins = []; - -exports.Octokit = Octokit; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 8713: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -var isPlainObject = __nccwpck_require__(3287); -var universalUserAgent = __nccwpck_require__(5030); - -function lowercaseKeys(object) { - if (!object) { - return {}; - } - - return Object.keys(object).reduce((newObj, key) => { - newObj[key.toLowerCase()] = object[key]; - return newObj; - }, {}); -} - -function mergeDeep(defaults, options) { - const result = Object.assign({}, defaults); - Object.keys(options).forEach(key => { - if (isPlainObject.isPlainObject(options[key])) { - if (!(key in defaults)) Object.assign(result, { - [key]: options[key] - });else result[key] = mergeDeep(defaults[key], options[key]); - } else { - Object.assign(result, { - [key]: options[key] - }); - } - }); - return result; -} - -function removeUndefinedProperties(obj) { - for (const key in obj) { - if (obj[key] === undefined) { - delete obj[key]; - } - } - - return obj; -} - -function merge(defaults, route, options) { - if (typeof route === "string") { - let [method, url] = route.split(" "); - options = Object.assign(url ? { - method, - url - } : { - url: method - }, options); - } else { - options = Object.assign({}, route); - } // lowercase header names before merging with defaults to avoid duplicates - - - options.headers = lowercaseKeys(options.headers); // remove properties with undefined values before merging - - removeUndefinedProperties(options); - removeUndefinedProperties(options.headers); - const mergedOptions = mergeDeep(defaults || {}, options); // mediaType.previews arrays are merged, instead of overwritten - - if (defaults && defaults.mediaType.previews.length) { - mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(preview => !mergedOptions.mediaType.previews.includes(preview)).concat(mergedOptions.mediaType.previews); - } - - mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map(preview => preview.replace(/-preview/, "")); - return mergedOptions; -} - -function addQueryParameters(url, parameters) { - const separator = /\?/.test(url) ? "&" : "?"; - const names = Object.keys(parameters); - - if (names.length === 0) { - return url; - } - - return url + separator + names.map(name => { - if (name === "q") { - return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+"); - } - - return `${name}=${encodeURIComponent(parameters[name])}`; - }).join("&"); -} - -const urlVariableRegex = /\{[^}]+\}/g; - -function removeNonChars(variableName) { - return variableName.replace(/^\W+|\W+$/g, "").split(/,/); -} - -function extractUrlVariableNames(url) { - const matches = url.match(urlVariableRegex); - - if (!matches) { - return []; - } - - return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []); -} - -function omit(object, keysToOmit) { - return Object.keys(object).filter(option => !keysToOmit.includes(option)).reduce((obj, key) => { - obj[key] = object[key]; - return obj; - }, {}); -} - -// Based on https://github.com/bramstein/url-template, licensed under BSD -// TODO: create separate package. -// -// Copyright (c) 2012-2014, Bram Stein -// All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. The name of the author may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* istanbul ignore file */ -function encodeReserved(str) { - return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) { - if (!/%[0-9A-Fa-f]/.test(part)) { - part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]"); - } - - return part; - }).join(""); -} - -function encodeUnreserved(str) { - return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { - return "%" + c.charCodeAt(0).toString(16).toUpperCase(); - }); -} - -function encodeValue(operator, value, key) { - value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value); - - if (key) { - return encodeUnreserved(key) + "=" + value; - } else { - return value; - } -} - -function isDefined(value) { - return value !== undefined && value !== null; -} - -function isKeyOperator(operator) { - return operator === ";" || operator === "&" || operator === "?"; -} - -function getValues(context, operator, key, modifier) { - var value = context[key], - result = []; - - if (isDefined(value) && value !== "") { - if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { - value = value.toString(); - - if (modifier && modifier !== "*") { - value = value.substring(0, parseInt(modifier, 10)); - } - - result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : "")); - } else { - if (modifier === "*") { - if (Array.isArray(value)) { - value.filter(isDefined).forEach(function (value) { - result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : "")); - }); - } else { - Object.keys(value).forEach(function (k) { - if (isDefined(value[k])) { - result.push(encodeValue(operator, value[k], k)); - } - }); - } - } else { - const tmp = []; - - if (Array.isArray(value)) { - value.filter(isDefined).forEach(function (value) { - tmp.push(encodeValue(operator, value)); - }); - } else { - Object.keys(value).forEach(function (k) { - if (isDefined(value[k])) { - tmp.push(encodeUnreserved(k)); - tmp.push(encodeValue(operator, value[k].toString())); - } - }); - } - - if (isKeyOperator(operator)) { - result.push(encodeUnreserved(key) + "=" + tmp.join(",")); - } else if (tmp.length !== 0) { - result.push(tmp.join(",")); - } - } - } - } else { - if (operator === ";") { - if (isDefined(value)) { - result.push(encodeUnreserved(key)); - } - } else if (value === "" && (operator === "&" || operator === "?")) { - result.push(encodeUnreserved(key) + "="); - } else if (value === "") { - result.push(""); - } - } - - return result; -} - -function parseUrl(template) { - return { - expand: expand.bind(null, template) - }; -} - -function expand(template, context) { - var operators = ["+", "#", ".", "/", ";", "?", "&"]; - return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) { - if (expression) { - let operator = ""; - const values = []; - - if (operators.indexOf(expression.charAt(0)) !== -1) { - operator = expression.charAt(0); - expression = expression.substr(1); - } - - expression.split(/,/g).forEach(function (variable) { - var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); - values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3])); - }); - - if (operator && operator !== "+") { - var separator = ","; - - if (operator === "?") { - separator = "&"; - } else if (operator !== "#") { - separator = operator; - } - - return (values.length !== 0 ? operator : "") + values.join(separator); - } else { - return values.join(","); - } - } else { - return encodeReserved(literal); - } - }); -} - -function parse(options) { - // https://fetch.spec.whatwg.org/#methods - let method = options.method.toUpperCase(); // replace :varname with {varname} to make it RFC 6570 compatible - - let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}"); - let headers = Object.assign({}, options.headers); - let body; - let parameters = omit(options, ["method", "baseUrl", "url", "headers", "request", "mediaType"]); // extract variable names from URL to calculate remaining variables later - - const urlVariableNames = extractUrlVariableNames(url); - url = parseUrl(url).expand(parameters); - - if (!/^http/.test(url)) { - url = options.baseUrl + url; - } - - const omittedParameters = Object.keys(options).filter(option => urlVariableNames.includes(option)).concat("baseUrl"); - const remainingParameters = omit(parameters, omittedParameters); - const isBinaryRequest = /application\/octet-stream/i.test(headers.accept); - - if (!isBinaryRequest) { - if (options.mediaType.format) { - // e.g. application/vnd.github.v3+json => application/vnd.github.v3.raw - headers.accept = headers.accept.split(/,/).map(preview => preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`)).join(","); - } - - if (options.mediaType.previews.length) { - const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || []; - headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map(preview => { - const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; - return `application/vnd.github.${preview}-preview${format}`; - }).join(","); - } - } // for GET/HEAD requests, set URL query parameters from remaining parameters - // for PATCH/POST/PUT/DELETE requests, set request body from remaining parameters - - - if (["GET", "HEAD"].includes(method)) { - url = addQueryParameters(url, remainingParameters); - } else { - if ("data" in remainingParameters) { - body = remainingParameters.data; - } else { - if (Object.keys(remainingParameters).length) { - body = remainingParameters; - } else { - headers["content-length"] = 0; - } - } - } // default content-type for JSON if body is set - - - if (!headers["content-type"] && typeof body !== "undefined") { - headers["content-type"] = "application/json; charset=utf-8"; - } // GitHub expects 'content-length: 0' header for PUT/PATCH requests without body. - // fetch does not allow to set `content-length` header, but we can set body to an empty string - - - if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") { - body = ""; - } // Only return body/request keys if present - - - return Object.assign({ - method, - url, - headers - }, typeof body !== "undefined" ? { - body - } : null, options.request ? { - request: options.request - } : null); -} - -function endpointWithDefaults(defaults, route, options) { - return parse(merge(defaults, route, options)); -} - -function withDefaults(oldDefaults, newDefaults) { - const DEFAULTS = merge(oldDefaults, newDefaults); - const endpoint = endpointWithDefaults.bind(null, DEFAULTS); - return Object.assign(endpoint, { - DEFAULTS, - defaults: withDefaults.bind(null, DEFAULTS), - merge: merge.bind(null, DEFAULTS), - parse - }); -} - -const VERSION = "6.0.12"; - -const userAgent = `octokit-endpoint.js/${VERSION} ${universalUserAgent.getUserAgent()}`; // DEFAULTS has all properties set that EndpointOptions has, except url. -// So we use RequestParameters and add method as additional required property. - -const DEFAULTS = { - method: "GET", - baseUrl: "https://api.github.com", - headers: { - accept: "application/vnd.github.v3+json", - "user-agent": userAgent - }, - mediaType: { - format: "", - previews: [] - } -}; - -const endpoint = withDefaults(null, DEFAULTS); - -exports.endpoint = endpoint; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 6422: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -var request = __nccwpck_require__(9353); -var universalUserAgent = __nccwpck_require__(5030); - -const VERSION = "4.8.0"; - -function _buildMessageForResponseErrors(data) { - return `Request failed due to following response errors:\n` + data.errors.map(e => ` - ${e.message}`).join("\n"); -} - -class GraphqlResponseError extends Error { - constructor(request, headers, response) { - super(_buildMessageForResponseErrors(response)); - this.request = request; - this.headers = headers; - this.response = response; - this.name = "GraphqlResponseError"; // Expose the errors and response data in their shorthand properties. - - this.errors = response.errors; - this.data = response.data; // Maintains proper stack trace (only available on V8) - - /* istanbul ignore next */ - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - } - -} - -const NON_VARIABLE_OPTIONS = ["method", "baseUrl", "url", "headers", "request", "query", "mediaType"]; -const FORBIDDEN_VARIABLE_OPTIONS = ["query", "method", "url"]; -const GHES_V3_SUFFIX_REGEX = /\/api\/v3\/?$/; -function graphql(request, query, options) { - if (options) { - if (typeof query === "string" && "query" in options) { - return Promise.reject(new Error(`[@octokit/graphql] "query" cannot be used as variable name`)); - } - - for (const key in options) { - if (!FORBIDDEN_VARIABLE_OPTIONS.includes(key)) continue; - return Promise.reject(new Error(`[@octokit/graphql] "${key}" cannot be used as variable name`)); - } - } - - const parsedOptions = typeof query === "string" ? Object.assign({ - query - }, options) : query; - const requestOptions = Object.keys(parsedOptions).reduce((result, key) => { - if (NON_VARIABLE_OPTIONS.includes(key)) { - result[key] = parsedOptions[key]; - return result; - } - - if (!result.variables) { - result.variables = {}; - } - - result.variables[key] = parsedOptions[key]; - return result; - }, {}); // workaround for GitHub Enterprise baseUrl set with /api/v3 suffix - // https://github.com/octokit/auth-app.js/issues/111#issuecomment-657610451 - - const baseUrl = parsedOptions.baseUrl || request.endpoint.DEFAULTS.baseUrl; - - if (GHES_V3_SUFFIX_REGEX.test(baseUrl)) { - requestOptions.url = baseUrl.replace(GHES_V3_SUFFIX_REGEX, "/api/graphql"); - } - - return request(requestOptions).then(response => { - if (response.data.errors) { - const headers = {}; - - for (const key of Object.keys(response.headers)) { - headers[key] = response.headers[key]; - } - - throw new GraphqlResponseError(requestOptions, headers, response.data); - } - - return response.data.data; - }); -} - -function withDefaults(request$1, newDefaults) { - const newRequest = request$1.defaults(newDefaults); - - const newApi = (query, options) => { - return graphql(newRequest, query, options); - }; - - return Object.assign(newApi, { - defaults: withDefaults.bind(null, newRequest), - endpoint: request.request.endpoint - }); -} - -const graphql$1 = withDefaults(request.request, { - headers: { - "user-agent": `octokit-graphql.js/${VERSION} ${universalUserAgent.getUserAgent()}` - }, - method: "POST", - url: "/graphql" -}); -function withCustomRequest(customRequest) { - return withDefaults(customRequest, { - method: "POST", - url: "/graphql" - }); -} - -exports.GraphqlResponseError = GraphqlResponseError; -exports.graphql = graphql$1; -exports.withCustomRequest = withCustomRequest; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 8945: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -const VERSION = "2.21.3"; - -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - enumerableOnly && (symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - })), keys.push.apply(keys, symbols); - } - - return keys; -} - -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = null != arguments[i] ? arguments[i] : {}; - i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { - _defineProperty(target, key, source[key]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - - return target; -} - -function _defineProperty(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; -} - -/** - * Some “list” response that can be paginated have a different response structure - * - * They have a `total_count` key in the response (search also has `incomplete_results`, - * /installation/repositories also has `repository_selection`), as well as a key with - * the list of the items which name varies from endpoint to endpoint. - * - * Octokit normalizes these responses so that paginated results are always returned following - * the same structure. One challenge is that if the list response has only one page, no Link - * header is provided, so this header alone is not sufficient to check wether a response is - * paginated or not. - * - * We check if a "total_count" key is present in the response data, but also make sure that - * a "url" property is not, as the "Get the combined status for a specific ref" endpoint would - * otherwise match: https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref - */ -function normalizePaginatedListResponse(response) { - // endpoints can respond with 204 if repository is empty - if (!response.data) { - return _objectSpread2(_objectSpread2({}, response), {}, { - data: [] - }); - } - - const responseNeedsNormalization = "total_count" in response.data && !("url" in response.data); - if (!responseNeedsNormalization) return response; // keep the additional properties intact as there is currently no other way - // to retrieve the same information. - - const incompleteResults = response.data.incomplete_results; - const repositorySelection = response.data.repository_selection; - const totalCount = response.data.total_count; - delete response.data.incomplete_results; - delete response.data.repository_selection; - delete response.data.total_count; - const namespaceKey = Object.keys(response.data)[0]; - const data = response.data[namespaceKey]; - response.data = data; - - if (typeof incompleteResults !== "undefined") { - response.data.incomplete_results = incompleteResults; - } - - if (typeof repositorySelection !== "undefined") { - response.data.repository_selection = repositorySelection; - } - - response.data.total_count = totalCount; - return response; -} - -function iterator(octokit, route, parameters) { - const options = typeof route === "function" ? route.endpoint(parameters) : octokit.request.endpoint(route, parameters); - const requestMethod = typeof route === "function" ? route : octokit.request; - const method = options.method; - const headers = options.headers; - let url = options.url; - return { - [Symbol.asyncIterator]: () => ({ - async next() { - if (!url) return { - done: true - }; - - try { - const response = await requestMethod({ - method, - url, - headers - }); - const normalizedResponse = normalizePaginatedListResponse(response); // `response.headers.link` format: - // '; rel="next", ; rel="last"' - // sets `url` to undefined if "next" URL is not present or `link` header is not set - - url = ((normalizedResponse.headers.link || "").match(/<([^>]+)>;\s*rel="next"/) || [])[1]; - return { - value: normalizedResponse - }; - } catch (error) { - if (error.status !== 409) throw error; - url = ""; - return { - value: { - status: 200, - headers: {}, - data: [] - } - }; - } - } - - }) - }; -} - -function paginate(octokit, route, parameters, mapFn) { - if (typeof parameters === "function") { - mapFn = parameters; - parameters = undefined; - } - - return gather(octokit, [], iterator(octokit, route, parameters)[Symbol.asyncIterator](), mapFn); -} - -function gather(octokit, results, iterator, mapFn) { - return iterator.next().then(result => { - if (result.done) { - return results; - } - - let earlyExit = false; - - function done() { - earlyExit = true; - } - - results = results.concat(mapFn ? mapFn(result.value, done) : result.value.data); - - if (earlyExit) { - return results; - } - - return gather(octokit, results, iterator, mapFn); - }); -} - -const composePaginateRest = Object.assign(paginate, { - iterator -}); - -const paginatingEndpoints = ["GET /app/hook/deliveries", "GET /app/installations", "GET /applications/grants", "GET /authorizations", "GET /enterprises/{enterprise}/actions/permissions/organizations", "GET /enterprises/{enterprise}/actions/runner-groups", "GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations", "GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners", "GET /enterprises/{enterprise}/actions/runners", "GET /enterprises/{enterprise}/audit-log", "GET /enterprises/{enterprise}/secret-scanning/alerts", "GET /enterprises/{enterprise}/settings/billing/advanced-security", "GET /events", "GET /gists", "GET /gists/public", "GET /gists/starred", "GET /gists/{gist_id}/comments", "GET /gists/{gist_id}/commits", "GET /gists/{gist_id}/forks", "GET /installation/repositories", "GET /issues", "GET /licenses", "GET /marketplace_listing/plans", "GET /marketplace_listing/plans/{plan_id}/accounts", "GET /marketplace_listing/stubbed/plans", "GET /marketplace_listing/stubbed/plans/{plan_id}/accounts", "GET /networks/{owner}/{repo}/events", "GET /notifications", "GET /organizations", "GET /orgs/{org}/actions/cache/usage-by-repository", "GET /orgs/{org}/actions/permissions/repositories", "GET /orgs/{org}/actions/runner-groups", "GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories", "GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners", "GET /orgs/{org}/actions/runners", "GET /orgs/{org}/actions/secrets", "GET /orgs/{org}/actions/secrets/{secret_name}/repositories", "GET /orgs/{org}/audit-log", "GET /orgs/{org}/blocks", "GET /orgs/{org}/code-scanning/alerts", "GET /orgs/{org}/codespaces", "GET /orgs/{org}/credential-authorizations", "GET /orgs/{org}/dependabot/secrets", "GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories", "GET /orgs/{org}/events", "GET /orgs/{org}/external-groups", "GET /orgs/{org}/failed_invitations", "GET /orgs/{org}/hooks", "GET /orgs/{org}/hooks/{hook_id}/deliveries", "GET /orgs/{org}/installations", "GET /orgs/{org}/invitations", "GET /orgs/{org}/invitations/{invitation_id}/teams", "GET /orgs/{org}/issues", "GET /orgs/{org}/members", "GET /orgs/{org}/migrations", "GET /orgs/{org}/migrations/{migration_id}/repositories", "GET /orgs/{org}/outside_collaborators", "GET /orgs/{org}/packages", "GET /orgs/{org}/packages/{package_type}/{package_name}/versions", "GET /orgs/{org}/projects", "GET /orgs/{org}/public_members", "GET /orgs/{org}/repos", "GET /orgs/{org}/secret-scanning/alerts", "GET /orgs/{org}/settings/billing/advanced-security", "GET /orgs/{org}/team-sync/groups", "GET /orgs/{org}/teams", "GET /orgs/{org}/teams/{team_slug}/discussions", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions", "GET /orgs/{org}/teams/{team_slug}/invitations", "GET /orgs/{org}/teams/{team_slug}/members", "GET /orgs/{org}/teams/{team_slug}/projects", "GET /orgs/{org}/teams/{team_slug}/repos", "GET /orgs/{org}/teams/{team_slug}/teams", "GET /projects/columns/{column_id}/cards", "GET /projects/{project_id}/collaborators", "GET /projects/{project_id}/columns", "GET /repos/{owner}/{repo}/actions/artifacts", "GET /repos/{owner}/{repo}/actions/caches", "GET /repos/{owner}/{repo}/actions/runners", "GET /repos/{owner}/{repo}/actions/runs", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs", "GET /repos/{owner}/{repo}/actions/secrets", "GET /repos/{owner}/{repo}/actions/workflows", "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", "GET /repos/{owner}/{repo}/assignees", "GET /repos/{owner}/{repo}/branches", "GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations", "GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs", "GET /repos/{owner}/{repo}/code-scanning/alerts", "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances", "GET /repos/{owner}/{repo}/code-scanning/analyses", "GET /repos/{owner}/{repo}/codespaces", "GET /repos/{owner}/{repo}/codespaces/devcontainers", "GET /repos/{owner}/{repo}/codespaces/secrets", "GET /repos/{owner}/{repo}/collaborators", "GET /repos/{owner}/{repo}/comments", "GET /repos/{owner}/{repo}/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/commits", "GET /repos/{owner}/{repo}/commits/{commit_sha}/comments", "GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls", "GET /repos/{owner}/{repo}/commits/{ref}/check-runs", "GET /repos/{owner}/{repo}/commits/{ref}/check-suites", "GET /repos/{owner}/{repo}/commits/{ref}/status", "GET /repos/{owner}/{repo}/commits/{ref}/statuses", "GET /repos/{owner}/{repo}/contributors", "GET /repos/{owner}/{repo}/dependabot/secrets", "GET /repos/{owner}/{repo}/deployments", "GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses", "GET /repos/{owner}/{repo}/environments", "GET /repos/{owner}/{repo}/events", "GET /repos/{owner}/{repo}/forks", "GET /repos/{owner}/{repo}/git/matching-refs/{ref}", "GET /repos/{owner}/{repo}/hooks", "GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", "GET /repos/{owner}/{repo}/invitations", "GET /repos/{owner}/{repo}/issues", "GET /repos/{owner}/{repo}/issues/comments", "GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/issues/events", "GET /repos/{owner}/{repo}/issues/{issue_number}/comments", "GET /repos/{owner}/{repo}/issues/{issue_number}/events", "GET /repos/{owner}/{repo}/issues/{issue_number}/labels", "GET /repos/{owner}/{repo}/issues/{issue_number}/reactions", "GET /repos/{owner}/{repo}/issues/{issue_number}/timeline", "GET /repos/{owner}/{repo}/keys", "GET /repos/{owner}/{repo}/labels", "GET /repos/{owner}/{repo}/milestones", "GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels", "GET /repos/{owner}/{repo}/notifications", "GET /repos/{owner}/{repo}/pages/builds", "GET /repos/{owner}/{repo}/projects", "GET /repos/{owner}/{repo}/pulls", "GET /repos/{owner}/{repo}/pulls/comments", "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/pulls/{pull_number}/comments", "GET /repos/{owner}/{repo}/pulls/{pull_number}/commits", "GET /repos/{owner}/{repo}/pulls/{pull_number}/files", "GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers", "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews", "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments", "GET /repos/{owner}/{repo}/releases", "GET /repos/{owner}/{repo}/releases/{release_id}/assets", "GET /repos/{owner}/{repo}/releases/{release_id}/reactions", "GET /repos/{owner}/{repo}/secret-scanning/alerts", "GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations", "GET /repos/{owner}/{repo}/stargazers", "GET /repos/{owner}/{repo}/subscribers", "GET /repos/{owner}/{repo}/tags", "GET /repos/{owner}/{repo}/teams", "GET /repos/{owner}/{repo}/topics", "GET /repositories", "GET /repositories/{repository_id}/environments/{environment_name}/secrets", "GET /search/code", "GET /search/commits", "GET /search/issues", "GET /search/labels", "GET /search/repositories", "GET /search/topics", "GET /search/users", "GET /teams/{team_id}/discussions", "GET /teams/{team_id}/discussions/{discussion_number}/comments", "GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions", "GET /teams/{team_id}/discussions/{discussion_number}/reactions", "GET /teams/{team_id}/invitations", "GET /teams/{team_id}/members", "GET /teams/{team_id}/projects", "GET /teams/{team_id}/repos", "GET /teams/{team_id}/teams", "GET /user/blocks", "GET /user/codespaces", "GET /user/codespaces/secrets", "GET /user/emails", "GET /user/followers", "GET /user/following", "GET /user/gpg_keys", "GET /user/installations", "GET /user/installations/{installation_id}/repositories", "GET /user/issues", "GET /user/keys", "GET /user/marketplace_purchases", "GET /user/marketplace_purchases/stubbed", "GET /user/memberships/orgs", "GET /user/migrations", "GET /user/migrations/{migration_id}/repositories", "GET /user/orgs", "GET /user/packages", "GET /user/packages/{package_type}/{package_name}/versions", "GET /user/public_emails", "GET /user/repos", "GET /user/repository_invitations", "GET /user/starred", "GET /user/subscriptions", "GET /user/teams", "GET /users", "GET /users/{username}/events", "GET /users/{username}/events/orgs/{org}", "GET /users/{username}/events/public", "GET /users/{username}/followers", "GET /users/{username}/following", "GET /users/{username}/gists", "GET /users/{username}/gpg_keys", "GET /users/{username}/keys", "GET /users/{username}/orgs", "GET /users/{username}/packages", "GET /users/{username}/projects", "GET /users/{username}/received_events", "GET /users/{username}/received_events/public", "GET /users/{username}/repos", "GET /users/{username}/starred", "GET /users/{username}/subscriptions"]; - -function isPaginatingEndpoint(arg) { - if (typeof arg === "string") { - return paginatingEndpoints.includes(arg); - } else { - return false; - } -} - -/** - * @param octokit Octokit instance - * @param options Options passed to Octokit constructor - */ - -function paginateRest(octokit) { - return { - paginate: Object.assign(paginate.bind(null, octokit), { - iterator: iterator.bind(null, octokit) - }) - }; -} -paginateRest.VERSION = VERSION; - -exports.composePaginateRest = composePaginateRest; -exports.isPaginatingEndpoint = isPaginatingEndpoint; -exports.paginateRest = paginateRest; -exports.paginatingEndpoints = paginatingEndpoints; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 7471: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var deprecation = __nccwpck_require__(8932); -var once = _interopDefault(__nccwpck_require__(1223)); - -const logOnceCode = once(deprecation => console.warn(deprecation)); -const logOnceHeaders = once(deprecation => console.warn(deprecation)); -/** - * Error with extra properties to help with debugging - */ - -class RequestError extends Error { - constructor(message, statusCode, options) { - super(message); // Maintains proper stack trace (only available on V8) - - /* istanbul ignore next */ - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = "HttpError"; - this.status = statusCode; - let headers; - - if ("headers" in options && typeof options.headers !== "undefined") { - headers = options.headers; - } - - if ("response" in options) { - this.response = options.response; - headers = options.response.headers; - } // redact request credentials without mutating original request options - - - const requestCopy = Object.assign({}, options.request); - - if (options.request.headers.authorization) { - requestCopy.headers = Object.assign({}, options.request.headers, { - authorization: options.request.headers.authorization.replace(/ .*$/, " [REDACTED]") - }); - } - - requestCopy.url = requestCopy.url // client_id & client_secret can be passed as URL query parameters to increase rate limit - // see https://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications - .replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]") // OAuth tokens can be passed as URL query parameters, although it is not recommended - // see https://developer.github.com/v3/#oauth2-token-sent-in-a-header - .replace(/\baccess_token=\w+/g, "access_token=[REDACTED]"); - this.request = requestCopy; // deprecations - - Object.defineProperty(this, "code", { - get() { - logOnceCode(new deprecation.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`.")); - return statusCode; - } - - }); - Object.defineProperty(this, "headers", { - get() { - logOnceHeaders(new deprecation.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`.")); - return headers || {}; - } - - }); - } - -} - -exports.RequestError = RequestError; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 9353: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var endpoint = __nccwpck_require__(8713); -var universalUserAgent = __nccwpck_require__(5030); -var isPlainObject = __nccwpck_require__(3287); -var nodeFetch = _interopDefault(__nccwpck_require__(467)); -var requestError = __nccwpck_require__(7471); - -const VERSION = "5.6.3"; - -function getBufferResponse(response) { - return response.arrayBuffer(); -} - -function fetchWrapper(requestOptions) { - const log = requestOptions.request && requestOptions.request.log ? requestOptions.request.log : console; - - if (isPlainObject.isPlainObject(requestOptions.body) || Array.isArray(requestOptions.body)) { - requestOptions.body = JSON.stringify(requestOptions.body); - } - - let headers = {}; - let status; - let url; - const fetch = requestOptions.request && requestOptions.request.fetch || nodeFetch; - return fetch(requestOptions.url, Object.assign({ - method: requestOptions.method, - body: requestOptions.body, - headers: requestOptions.headers, - redirect: requestOptions.redirect - }, // `requestOptions.request.agent` type is incompatible - // see https://github.com/octokit/types.ts/pull/264 - requestOptions.request)).then(async response => { - url = response.url; - status = response.status; - - for (const keyAndValue of response.headers) { - headers[keyAndValue[0]] = keyAndValue[1]; - } - - if ("deprecation" in headers) { - const matches = headers.link && headers.link.match(/<([^>]+)>; rel="deprecation"/); - const deprecationLink = matches && matches.pop(); - log.warn(`[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}`); - } - - if (status === 204 || status === 205) { - return; - } // GitHub API returns 200 for HEAD requests - - - if (requestOptions.method === "HEAD") { - if (status < 400) { - return; - } - - throw new requestError.RequestError(response.statusText, status, { - response: { - url, - status, - headers, - data: undefined - }, - request: requestOptions - }); - } - - if (status === 304) { - throw new requestError.RequestError("Not modified", status, { - response: { - url, - status, - headers, - data: await getResponseData(response) - }, - request: requestOptions - }); - } - - if (status >= 400) { - const data = await getResponseData(response); - const error = new requestError.RequestError(toErrorMessage(data), status, { - response: { - url, - status, - headers, - data - }, - request: requestOptions - }); - throw error; - } - - return getResponseData(response); - }).then(data => { - return { - status, - url, - headers, - data - }; - }).catch(error => { - if (error instanceof requestError.RequestError) throw error; - throw new requestError.RequestError(error.message, 500, { - request: requestOptions - }); - }); -} - -async function getResponseData(response) { - const contentType = response.headers.get("content-type"); - - if (/application\/json/.test(contentType)) { - return response.json(); - } - - if (!contentType || /^text\/|charset=utf-8$/.test(contentType)) { - return response.text(); - } - - return getBufferResponse(response); -} - -function toErrorMessage(data) { - if (typeof data === "string") return data; // istanbul ignore else - just in case - - if ("message" in data) { - if (Array.isArray(data.errors)) { - return `${data.message}: ${data.errors.map(JSON.stringify).join(", ")}`; - } - - return data.message; - } // istanbul ignore next - just in case - - - return `Unknown error: ${JSON.stringify(data)}`; -} - -function withDefaults(oldEndpoint, newDefaults) { - const endpoint = oldEndpoint.defaults(newDefaults); - - const newApi = function (route, parameters) { - const endpointOptions = endpoint.merge(route, parameters); - - if (!endpointOptions.request || !endpointOptions.request.hook) { - return fetchWrapper(endpoint.parse(endpointOptions)); - } - - const request = (route, parameters) => { - return fetchWrapper(endpoint.parse(endpoint.merge(route, parameters))); - }; - - Object.assign(request, { - endpoint, - defaults: withDefaults.bind(null, endpoint) - }); - return endpointOptions.request.hook(request, endpointOptions); - }; - - return Object.assign(newApi, { - endpoint, - defaults: withDefaults.bind(null, endpoint) - }); -} - -const request = withDefaults(endpoint.endpoint, { - headers: { - "user-agent": `octokit-request.js/${VERSION} ${universalUserAgent.getUserAgent()}` - } -}); - -exports.request = request; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 5526: -/***/ (function(__unused_webpack_module, exports) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.PersonalAccessTokenCredentialHandler = exports.BearerCredentialHandler = exports.BasicCredentialHandler = void 0; -class BasicCredentialHandler { - constructor(username, password) { - this.username = username; - this.password = password; - } - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.BasicCredentialHandler = BasicCredentialHandler; -class BearerCredentialHandler { - constructor(token) { - this.token = token; - } - // currently implements pre-authorization - // TODO: support preAuth = false where it hooks on 401 - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Bearer ${this.token}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.BearerCredentialHandler = BearerCredentialHandler; -class PersonalAccessTokenCredentialHandler { - constructor(token) { - this.token = token; - } - // currently implements pre-authorization - // TODO: support preAuth = false where it hooks on 401 - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; -//# sourceMappingURL=auth.js.map - -/***/ }), - -/***/ 6255: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; -const http = __importStar(__nccwpck_require__(3685)); -const https = __importStar(__nccwpck_require__(5687)); -const pm = __importStar(__nccwpck_require__(9835)); -const tunnel = __importStar(__nccwpck_require__(4294)); -var HttpCodes; -(function (HttpCodes) { - HttpCodes[HttpCodes["OK"] = 200] = "OK"; - HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; - HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; - HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; - HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; - HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; - HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; - HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; - HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; - HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; - HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; - HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; - HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; - HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; - HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; - HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; - HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; - HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; - HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; - HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; - HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; - HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; - HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; - HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; - HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; - HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; - HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; -})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); -var Headers; -(function (Headers) { - Headers["Accept"] = "accept"; - Headers["ContentType"] = "content-type"; -})(Headers = exports.Headers || (exports.Headers = {})); -var MediaTypes; -(function (MediaTypes) { - MediaTypes["ApplicationJson"] = "application/json"; -})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {})); -/** - * Returns the proxy URL, depending upon the supplied url and proxy environment variables. - * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com - */ -function getProxyUrl(serverUrl) { - const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); - return proxyUrl ? proxyUrl.href : ''; -} -exports.getProxyUrl = getProxyUrl; -const HttpRedirectCodes = [ - HttpCodes.MovedPermanently, - HttpCodes.ResourceMoved, - HttpCodes.SeeOther, - HttpCodes.TemporaryRedirect, - HttpCodes.PermanentRedirect -]; -const HttpResponseRetryCodes = [ - HttpCodes.BadGateway, - HttpCodes.ServiceUnavailable, - HttpCodes.GatewayTimeout -]; -const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; -const ExponentialBackoffCeiling = 10; -const ExponentialBackoffTimeSlice = 5; -class HttpClientError extends Error { - constructor(message, statusCode) { - super(message); - this.name = 'HttpClientError'; - this.statusCode = statusCode; - Object.setPrototypeOf(this, HttpClientError.prototype); - } -} -exports.HttpClientError = HttpClientError; -class HttpClientResponse { - constructor(message) { - this.message = message; - } - readBody() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { - let output = Buffer.alloc(0); - this.message.on('data', (chunk) => { - output = Buffer.concat([output, chunk]); - }); - this.message.on('end', () => { - resolve(output.toString()); - }); - })); - }); - } -} -exports.HttpClientResponse = HttpClientResponse; -function isHttps(requestUrl) { - const parsedUrl = new URL(requestUrl); - return parsedUrl.protocol === 'https:'; -} -exports.isHttps = isHttps; -class HttpClient { - constructor(userAgent, handlers, requestOptions) { - this._ignoreSslError = false; - this._allowRedirects = true; - this._allowRedirectDowngrade = false; - this._maxRedirects = 50; - this._allowRetries = false; - this._maxRetries = 1; - this._keepAlive = false; - this._disposed = false; - this.userAgent = userAgent; - this.handlers = handlers || []; - this.requestOptions = requestOptions; - if (requestOptions) { - if (requestOptions.ignoreSslError != null) { - this._ignoreSslError = requestOptions.ignoreSslError; - } - this._socketTimeout = requestOptions.socketTimeout; - if (requestOptions.allowRedirects != null) { - this._allowRedirects = requestOptions.allowRedirects; - } - if (requestOptions.allowRedirectDowngrade != null) { - this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; - } - if (requestOptions.maxRedirects != null) { - this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); - } - if (requestOptions.keepAlive != null) { - this._keepAlive = requestOptions.keepAlive; - } - if (requestOptions.allowRetries != null) { - this._allowRetries = requestOptions.allowRetries; - } - if (requestOptions.maxRetries != null) { - this._maxRetries = requestOptions.maxRetries; - } - } - } - options(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); - }); - } - get(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('GET', requestUrl, null, additionalHeaders || {}); - }); - } - del(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('DELETE', requestUrl, null, additionalHeaders || {}); - }); - } - post(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('POST', requestUrl, data, additionalHeaders || {}); - }); - } - patch(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('PATCH', requestUrl, data, additionalHeaders || {}); - }); - } - put(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('PUT', requestUrl, data, additionalHeaders || {}); - }); - } - head(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('HEAD', requestUrl, null, additionalHeaders || {}); - }); - } - sendStream(verb, requestUrl, stream, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request(verb, requestUrl, stream, additionalHeaders); - }); - } - /** - * Gets a typed object from an endpoint - * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise - */ - getJson(requestUrl, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - const res = yield this.get(requestUrl, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - postJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.post(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - putJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.put(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - patchJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.patch(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - /** - * Makes a raw http request. - * All other methods such as get, post, patch, and request ultimately call this. - * Prefer get, del, post and patch - */ - request(verb, requestUrl, data, headers) { - return __awaiter(this, void 0, void 0, function* () { - if (this._disposed) { - throw new Error('Client has already been disposed.'); - } - const parsedUrl = new URL(requestUrl); - let info = this._prepareRequest(verb, parsedUrl, headers); - // Only perform retries on reads since writes may not be idempotent. - const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) - ? this._maxRetries + 1 - : 1; - let numTries = 0; - let response; - do { - response = yield this.requestRaw(info, data); - // Check if it's an authentication challenge - if (response && - response.message && - response.message.statusCode === HttpCodes.Unauthorized) { - let authenticationHandler; - for (const handler of this.handlers) { - if (handler.canHandleAuthentication(response)) { - authenticationHandler = handler; - break; - } - } - if (authenticationHandler) { - return authenticationHandler.handleAuthentication(this, info, data); - } - else { - // We have received an unauthorized response but have no handlers to handle it. - // Let the response return to the caller. - return response; - } - } - let redirectsRemaining = this._maxRedirects; - while (response.message.statusCode && - HttpRedirectCodes.includes(response.message.statusCode) && - this._allowRedirects && - redirectsRemaining > 0) { - const redirectUrl = response.message.headers['location']; - if (!redirectUrl) { - // if there's no location to redirect to, we won't - break; - } - const parsedRedirectUrl = new URL(redirectUrl); - if (parsedUrl.protocol === 'https:' && - parsedUrl.protocol !== parsedRedirectUrl.protocol && - !this._allowRedirectDowngrade) { - throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); - } - // we need to finish reading the response before reassigning response - // which will leak the open socket. - yield response.readBody(); - // strip authorization header if redirected to a different hostname - if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { - for (const header in headers) { - // header names are case insensitive - if (header.toLowerCase() === 'authorization') { - delete headers[header]; - } - } - } - // let's make the request with the new redirectUrl - info = this._prepareRequest(verb, parsedRedirectUrl, headers); - response = yield this.requestRaw(info, data); - redirectsRemaining--; - } - if (!response.message.statusCode || - !HttpResponseRetryCodes.includes(response.message.statusCode)) { - // If not a retry code, return immediately instead of retrying - return response; - } - numTries += 1; - if (numTries < maxTries) { - yield response.readBody(); - yield this._performExponentialBackoff(numTries); - } - } while (numTries < maxTries); - return response; - }); - } - /** - * Needs to be called if keepAlive is set to true in request options. - */ - dispose() { - if (this._agent) { - this._agent.destroy(); - } - this._disposed = true; - } - /** - * Raw request. - * @param info - * @param data - */ - requestRaw(info, data) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - function callbackForResult(err, res) { - if (err) { - reject(err); - } - else if (!res) { - // If `err` is not passed, then `res` must be passed. - reject(new Error('Unknown error')); - } - else { - resolve(res); - } - } - this.requestRawWithCallback(info, data, callbackForResult); - }); - }); - } - /** - * Raw request with callback. - * @param info - * @param data - * @param onResult - */ - requestRawWithCallback(info, data, onResult) { - if (typeof data === 'string') { - if (!info.options.headers) { - info.options.headers = {}; - } - info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); - } - let callbackCalled = false; - function handleResult(err, res) { - if (!callbackCalled) { - callbackCalled = true; - onResult(err, res); - } - } - const req = info.httpModule.request(info.options, (msg) => { - const res = new HttpClientResponse(msg); - handleResult(undefined, res); - }); - let socket; - req.on('socket', sock => { - socket = sock; - }); - // If we ever get disconnected, we want the socket to timeout eventually - req.setTimeout(this._socketTimeout || 3 * 60000, () => { - if (socket) { - socket.end(); - } - handleResult(new Error(`Request timeout: ${info.options.path}`)); - }); - req.on('error', function (err) { - // err has statusCode property - // res should have headers - handleResult(err); - }); - if (data && typeof data === 'string') { - req.write(data, 'utf8'); - } - if (data && typeof data !== 'string') { - data.on('close', function () { - req.end(); - }); - data.pipe(req); - } - else { - req.end(); - } - } - /** - * Gets an http agent. This function is useful when you need an http agent that handles - * routing through a proxy server - depending upon the url and proxy environment variables. - * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com - */ - getAgent(serverUrl) { - const parsedUrl = new URL(serverUrl); - return this._getAgent(parsedUrl); - } - _prepareRequest(method, requestUrl, headers) { - const info = {}; - info.parsedUrl = requestUrl; - const usingSsl = info.parsedUrl.protocol === 'https:'; - info.httpModule = usingSsl ? https : http; - const defaultPort = usingSsl ? 443 : 80; - info.options = {}; - info.options.host = info.parsedUrl.hostname; - info.options.port = info.parsedUrl.port - ? parseInt(info.parsedUrl.port) - : defaultPort; - info.options.path = - (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); - info.options.method = method; - info.options.headers = this._mergeHeaders(headers); - if (this.userAgent != null) { - info.options.headers['user-agent'] = this.userAgent; - } - info.options.agent = this._getAgent(info.parsedUrl); - // gives handlers an opportunity to participate - if (this.handlers) { - for (const handler of this.handlers) { - handler.prepareRequest(info.options); - } - } - return info; - } - _mergeHeaders(headers) { - if (this.requestOptions && this.requestOptions.headers) { - return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers || {})); - } - return lowercaseKeys(headers || {}); - } - _getExistingOrDefaultHeader(additionalHeaders, header, _default) { - let clientHeader; - if (this.requestOptions && this.requestOptions.headers) { - clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; - } - return additionalHeaders[header] || clientHeader || _default; - } - _getAgent(parsedUrl) { - let agent; - const proxyUrl = pm.getProxyUrl(parsedUrl); - const useProxy = proxyUrl && proxyUrl.hostname; - if (this._keepAlive && useProxy) { - agent = this._proxyAgent; - } - if (this._keepAlive && !useProxy) { - agent = this._agent; - } - // if agent is already assigned use that agent. - if (agent) { - return agent; - } - const usingSsl = parsedUrl.protocol === 'https:'; - let maxSockets = 100; - if (this.requestOptions) { - maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; - } - // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. - if (proxyUrl && proxyUrl.hostname) { - const agentOptions = { - maxSockets, - keepAlive: this._keepAlive, - proxy: Object.assign(Object.assign({}, ((proxyUrl.username || proxyUrl.password) && { - proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` - })), { host: proxyUrl.hostname, port: proxyUrl.port }) - }; - let tunnelAgent; - const overHttps = proxyUrl.protocol === 'https:'; - if (usingSsl) { - tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; - } - else { - tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; - } - agent = tunnelAgent(agentOptions); - this._proxyAgent = agent; - } - // if reusing agent across request and tunneling agent isn't assigned create a new agent - if (this._keepAlive && !agent) { - const options = { keepAlive: this._keepAlive, maxSockets }; - agent = usingSsl ? new https.Agent(options) : new http.Agent(options); - this._agent = agent; - } - // if not using private agent and tunnel agent isn't setup then use global agent - if (!agent) { - agent = usingSsl ? https.globalAgent : http.globalAgent; - } - if (usingSsl && this._ignoreSslError) { - // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process - // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options - // we have to cast it to any and change it directly - agent.options = Object.assign(agent.options || {}, { - rejectUnauthorized: false - }); - } - return agent; - } - _performExponentialBackoff(retryNumber) { - return __awaiter(this, void 0, void 0, function* () { - retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); - const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); - return new Promise(resolve => setTimeout(() => resolve(), ms)); - }); - } - _processResponse(res, options) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - const statusCode = res.message.statusCode || 0; - const response = { - statusCode, - result: null, - headers: {} - }; - // not found leads to null obj returned - if (statusCode === HttpCodes.NotFound) { - resolve(response); - } - // get the result from the body - function dateTimeDeserializer(key, value) { - if (typeof value === 'string') { - const a = new Date(value); - if (!isNaN(a.valueOf())) { - return a; - } - } - return value; - } - let obj; - let contents; - try { - contents = yield res.readBody(); - if (contents && contents.length > 0) { - if (options && options.deserializeDates) { - obj = JSON.parse(contents, dateTimeDeserializer); - } - else { - obj = JSON.parse(contents); - } - response.result = obj; - } - response.headers = res.message.headers; - } - catch (err) { - // Invalid resource (contents not json); leaving result obj null - } - // note that 3xx redirects are handled by the http layer. - if (statusCode > 299) { - let msg; - // if exception/error in body, attempt to get better error - if (obj && obj.message) { - msg = obj.message; - } - else if (contents && contents.length > 0) { - // it may be the case that the exception is in the body message as string - msg = contents; - } - else { - msg = `Failed request: (${statusCode})`; - } - const err = new HttpClientError(msg, statusCode); - err.result = response.result; - reject(err); - } - else { - resolve(response); - } - })); - }); - } -} -exports.HttpClient = HttpClient; -const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 9835: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.checkBypass = exports.getProxyUrl = void 0; -function getProxyUrl(reqUrl) { - const usingSsl = reqUrl.protocol === 'https:'; - if (checkBypass(reqUrl)) { - return undefined; - } - const proxyVar = (() => { - if (usingSsl) { - return process.env['https_proxy'] || process.env['HTTPS_PROXY']; - } - else { - return process.env['http_proxy'] || process.env['HTTP_PROXY']; - } - })(); - if (proxyVar) { - return new URL(proxyVar); - } - else { - return undefined; - } -} -exports.getProxyUrl = getProxyUrl; -function checkBypass(reqUrl) { - if (!reqUrl.hostname) { - return false; - } - const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; - if (!noProxy) { - return false; - } - // Determine the request port - let reqPort; - if (reqUrl.port) { - reqPort = Number(reqUrl.port); - } - else if (reqUrl.protocol === 'http:') { - reqPort = 80; - } - else if (reqUrl.protocol === 'https:') { - reqPort = 443; - } - // Format the request hostname and hostname with port - const upperReqHosts = [reqUrl.hostname.toUpperCase()]; - if (typeof reqPort === 'number') { - upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); - } - // Compare request host against noproxy - for (const upperNoProxyItem of noProxy - .split(',') - .map(x => x.trim().toUpperCase()) - .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { - return true; - } - } - return false; -} -exports.checkBypass = checkBypass; -//# sourceMappingURL=proxy.js.map - -/***/ }), - -/***/ 4193: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -const VERSION = "3.1.0"; - -/** - * Some “list” response that can be paginated have a different response structure - * - * They have a `total_count` key in the response (search also has `incomplete_results`, - * /installation/repositories also has `repository_selection`), as well as a key with - * the list of the items which name varies from endpoint to endpoint. - * - * Octokit normalizes these responses so that paginated results are always returned following - * the same structure. One challenge is that if the list response has only one page, no Link - * header is provided, so this header alone is not sufficient to check wether a response is - * paginated or not. - * - * We check if a "total_count" key is present in the response data, but also make sure that - * a "url" property is not, as the "Get the combined status for a specific ref" endpoint would - * otherwise match: https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref - */ -function normalizePaginatedListResponse(response) { - // endpoints can respond with 204 if repository is empty - if (!response.data) { - return { ...response, - data: [] - }; - } - - const responseNeedsNormalization = "total_count" in response.data && !("url" in response.data); - if (!responseNeedsNormalization) return response; // keep the additional properties intact as there is currently no other way - // to retrieve the same information. - - const incompleteResults = response.data.incomplete_results; - const repositorySelection = response.data.repository_selection; - const totalCount = response.data.total_count; - delete response.data.incomplete_results; - delete response.data.repository_selection; - delete response.data.total_count; - const namespaceKey = Object.keys(response.data)[0]; - const data = response.data[namespaceKey]; - response.data = data; - - if (typeof incompleteResults !== "undefined") { - response.data.incomplete_results = incompleteResults; - } - - if (typeof repositorySelection !== "undefined") { - response.data.repository_selection = repositorySelection; - } - - response.data.total_count = totalCount; - return response; -} - -function iterator(octokit, route, parameters) { - const options = typeof route === "function" ? route.endpoint(parameters) : octokit.request.endpoint(route, parameters); - const requestMethod = typeof route === "function" ? route : octokit.request; - const method = options.method; - const headers = options.headers; - let url = options.url; - return { - [Symbol.asyncIterator]: () => ({ - async next() { - if (!url) return { - done: true - }; - - try { - const response = await requestMethod({ - method, - url, - headers - }); - const normalizedResponse = normalizePaginatedListResponse(response); // `response.headers.link` format: - // '; rel="next", ; rel="last"' - // sets `url` to undefined if "next" URL is not present or `link` header is not set - - url = ((normalizedResponse.headers.link || "").match(/<([^>]+)>;\s*rel="next"/) || [])[1]; - return { - value: normalizedResponse - }; - } catch (error) { - if (error.status !== 409) throw error; - url = ""; - return { - value: { - status: 200, - headers: {}, - data: [] - } - }; - } - } - - }) - }; -} - -function paginate(octokit, route, parameters, mapFn) { - if (typeof parameters === "function") { - mapFn = parameters; - parameters = undefined; - } - - return gather(octokit, [], iterator(octokit, route, parameters)[Symbol.asyncIterator](), mapFn); -} - -function gather(octokit, results, iterator, mapFn) { - return iterator.next().then(result => { - if (result.done) { - return results; - } - - let earlyExit = false; - - function done() { - earlyExit = true; - } - - results = results.concat(mapFn ? mapFn(result.value, done) : result.value.data); - - if (earlyExit) { - return results; - } - - return gather(octokit, results, iterator, mapFn); - }); -} - -const composePaginateRest = Object.assign(paginate, { - iterator -}); - -const paginatingEndpoints = ["GET /app/hook/deliveries", "GET /app/installations", "GET /applications/grants", "GET /authorizations", "GET /enterprises/{enterprise}/actions/permissions/organizations", "GET /enterprises/{enterprise}/actions/runner-groups", "GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/organizations", "GET /enterprises/{enterprise}/actions/runner-groups/{runner_group_id}/runners", "GET /enterprises/{enterprise}/actions/runners", "GET /enterprises/{enterprise}/audit-log", "GET /enterprises/{enterprise}/code-scanning/alerts", "GET /enterprises/{enterprise}/secret-scanning/alerts", "GET /enterprises/{enterprise}/settings/billing/advanced-security", "GET /events", "GET /gists", "GET /gists/public", "GET /gists/starred", "GET /gists/{gist_id}/comments", "GET /gists/{gist_id}/commits", "GET /gists/{gist_id}/forks", "GET /installation/repositories", "GET /issues", "GET /licenses", "GET /marketplace_listing/plans", "GET /marketplace_listing/plans/{plan_id}/accounts", "GET /marketplace_listing/stubbed/plans", "GET /marketplace_listing/stubbed/plans/{plan_id}/accounts", "GET /networks/{owner}/{repo}/events", "GET /notifications", "GET /organizations", "GET /orgs/{org}/actions/cache/usage-by-repository", "GET /orgs/{org}/actions/permissions/repositories", "GET /orgs/{org}/actions/runner-groups", "GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories", "GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners", "GET /orgs/{org}/actions/runners", "GET /orgs/{org}/actions/secrets", "GET /orgs/{org}/actions/secrets/{secret_name}/repositories", "GET /orgs/{org}/audit-log", "GET /orgs/{org}/blocks", "GET /orgs/{org}/code-scanning/alerts", "GET /orgs/{org}/codespaces", "GET /orgs/{org}/credential-authorizations", "GET /orgs/{org}/dependabot/secrets", "GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories", "GET /orgs/{org}/events", "GET /orgs/{org}/external-groups", "GET /orgs/{org}/failed_invitations", "GET /orgs/{org}/hooks", "GET /orgs/{org}/hooks/{hook_id}/deliveries", "GET /orgs/{org}/installations", "GET /orgs/{org}/invitations", "GET /orgs/{org}/invitations/{invitation_id}/teams", "GET /orgs/{org}/issues", "GET /orgs/{org}/members", "GET /orgs/{org}/migrations", "GET /orgs/{org}/migrations/{migration_id}/repositories", "GET /orgs/{org}/outside_collaborators", "GET /orgs/{org}/packages", "GET /orgs/{org}/packages/{package_type}/{package_name}/versions", "GET /orgs/{org}/projects", "GET /orgs/{org}/public_members", "GET /orgs/{org}/repos", "GET /orgs/{org}/secret-scanning/alerts", "GET /orgs/{org}/settings/billing/advanced-security", "GET /orgs/{org}/team-sync/groups", "GET /orgs/{org}/teams", "GET /orgs/{org}/teams/{team_slug}/discussions", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions", "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions", "GET /orgs/{org}/teams/{team_slug}/invitations", "GET /orgs/{org}/teams/{team_slug}/members", "GET /orgs/{org}/teams/{team_slug}/projects", "GET /orgs/{org}/teams/{team_slug}/repos", "GET /orgs/{org}/teams/{team_slug}/teams", "GET /projects/columns/{column_id}/cards", "GET /projects/{project_id}/collaborators", "GET /projects/{project_id}/columns", "GET /repos/{owner}/{repo}/actions/artifacts", "GET /repos/{owner}/{repo}/actions/caches", "GET /repos/{owner}/{repo}/actions/runners", "GET /repos/{owner}/{repo}/actions/runs", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs", "GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs", "GET /repos/{owner}/{repo}/actions/secrets", "GET /repos/{owner}/{repo}/actions/workflows", "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", "GET /repos/{owner}/{repo}/assignees", "GET /repos/{owner}/{repo}/branches", "GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations", "GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs", "GET /repos/{owner}/{repo}/code-scanning/alerts", "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances", "GET /repos/{owner}/{repo}/code-scanning/analyses", "GET /repos/{owner}/{repo}/codespaces", "GET /repos/{owner}/{repo}/codespaces/devcontainers", "GET /repos/{owner}/{repo}/codespaces/secrets", "GET /repos/{owner}/{repo}/collaborators", "GET /repos/{owner}/{repo}/comments", "GET /repos/{owner}/{repo}/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/commits", "GET /repos/{owner}/{repo}/commits/{commit_sha}/comments", "GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls", "GET /repos/{owner}/{repo}/commits/{ref}/check-runs", "GET /repos/{owner}/{repo}/commits/{ref}/check-suites", "GET /repos/{owner}/{repo}/commits/{ref}/status", "GET /repos/{owner}/{repo}/commits/{ref}/statuses", "GET /repos/{owner}/{repo}/contributors", "GET /repos/{owner}/{repo}/dependabot/secrets", "GET /repos/{owner}/{repo}/deployments", "GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses", "GET /repos/{owner}/{repo}/environments", "GET /repos/{owner}/{repo}/events", "GET /repos/{owner}/{repo}/forks", "GET /repos/{owner}/{repo}/git/matching-refs/{ref}", "GET /repos/{owner}/{repo}/hooks", "GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", "GET /repos/{owner}/{repo}/invitations", "GET /repos/{owner}/{repo}/issues", "GET /repos/{owner}/{repo}/issues/comments", "GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/issues/events", "GET /repos/{owner}/{repo}/issues/{issue_number}/comments", "GET /repos/{owner}/{repo}/issues/{issue_number}/events", "GET /repos/{owner}/{repo}/issues/{issue_number}/labels", "GET /repos/{owner}/{repo}/issues/{issue_number}/reactions", "GET /repos/{owner}/{repo}/issues/{issue_number}/timeline", "GET /repos/{owner}/{repo}/keys", "GET /repos/{owner}/{repo}/labels", "GET /repos/{owner}/{repo}/milestones", "GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels", "GET /repos/{owner}/{repo}/notifications", "GET /repos/{owner}/{repo}/pages/builds", "GET /repos/{owner}/{repo}/projects", "GET /repos/{owner}/{repo}/pulls", "GET /repos/{owner}/{repo}/pulls/comments", "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions", "GET /repos/{owner}/{repo}/pulls/{pull_number}/comments", "GET /repos/{owner}/{repo}/pulls/{pull_number}/commits", "GET /repos/{owner}/{repo}/pulls/{pull_number}/files", "GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers", "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews", "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments", "GET /repos/{owner}/{repo}/releases", "GET /repos/{owner}/{repo}/releases/{release_id}/assets", "GET /repos/{owner}/{repo}/releases/{release_id}/reactions", "GET /repos/{owner}/{repo}/secret-scanning/alerts", "GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations", "GET /repos/{owner}/{repo}/stargazers", "GET /repos/{owner}/{repo}/subscribers", "GET /repos/{owner}/{repo}/tags", "GET /repos/{owner}/{repo}/teams", "GET /repos/{owner}/{repo}/topics", "GET /repositories", "GET /repositories/{repository_id}/environments/{environment_name}/secrets", "GET /search/code", "GET /search/commits", "GET /search/issues", "GET /search/labels", "GET /search/repositories", "GET /search/topics", "GET /search/users", "GET /teams/{team_id}/discussions", "GET /teams/{team_id}/discussions/{discussion_number}/comments", "GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions", "GET /teams/{team_id}/discussions/{discussion_number}/reactions", "GET /teams/{team_id}/invitations", "GET /teams/{team_id}/members", "GET /teams/{team_id}/projects", "GET /teams/{team_id}/repos", "GET /teams/{team_id}/teams", "GET /user/blocks", "GET /user/codespaces", "GET /user/codespaces/secrets", "GET /user/emails", "GET /user/followers", "GET /user/following", "GET /user/gpg_keys", "GET /user/installations", "GET /user/installations/{installation_id}/repositories", "GET /user/issues", "GET /user/keys", "GET /user/marketplace_purchases", "GET /user/marketplace_purchases/stubbed", "GET /user/memberships/orgs", "GET /user/migrations", "GET /user/migrations/{migration_id}/repositories", "GET /user/orgs", "GET /user/packages", "GET /user/packages/{package_type}/{package_name}/versions", "GET /user/public_emails", "GET /user/repos", "GET /user/repository_invitations", "GET /user/starred", "GET /user/subscriptions", "GET /user/teams", "GET /users", "GET /users/{username}/events", "GET /users/{username}/events/orgs/{org}", "GET /users/{username}/events/public", "GET /users/{username}/followers", "GET /users/{username}/following", "GET /users/{username}/gists", "GET /users/{username}/gpg_keys", "GET /users/{username}/keys", "GET /users/{username}/orgs", "GET /users/{username}/packages", "GET /users/{username}/projects", "GET /users/{username}/received_events", "GET /users/{username}/received_events/public", "GET /users/{username}/repos", "GET /users/{username}/starred", "GET /users/{username}/subscriptions"]; - -function isPaginatingEndpoint(arg) { - if (typeof arg === "string") { - return paginatingEndpoints.includes(arg); - } else { - return false; - } -} - -/** - * @param octokit Octokit instance - * @param options Options passed to Octokit constructor - */ - -function paginateRest(octokit) { - return { - paginate: Object.assign(paginate.bind(null, octokit), { - iterator: iterator.bind(null, octokit) - }) - }; -} -paginateRest.VERSION = VERSION; - -exports.composePaginateRest = composePaginateRest; -exports.isPaginatingEndpoint = isPaginatingEndpoint; -exports.paginateRest = paginateRest; -exports.paginatingEndpoints = paginatingEndpoints; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 3044: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function ownKeys(object, enumerableOnly) { - var keys = Object.keys(object); - - if (Object.getOwnPropertySymbols) { - var symbols = Object.getOwnPropertySymbols(object); - - if (enumerableOnly) { - symbols = symbols.filter(function (sym) { - return Object.getOwnPropertyDescriptor(object, sym).enumerable; - }); - } - - keys.push.apply(keys, symbols); - } - - return keys; -} - -function _objectSpread2(target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i] != null ? arguments[i] : {}; - - if (i % 2) { - ownKeys(Object(source), true).forEach(function (key) { - _defineProperty(target, key, source[key]); - }); - } else if (Object.getOwnPropertyDescriptors) { - Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); - } else { - ownKeys(Object(source)).forEach(function (key) { - Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); - }); - } - } - - return target; -} - -function _defineProperty(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; -} - -const Endpoints = { - actions: { - addCustomLabelsToSelfHostedRunnerForOrg: ["POST /orgs/{org}/actions/runners/{runner_id}/labels"], - addCustomLabelsToSelfHostedRunnerForRepo: ["POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"], - addSelectedRepoToOrgSecret: ["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"], - approveWorkflowRun: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve"], - cancelWorkflowRun: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel"], - createOrUpdateEnvironmentSecret: ["PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"], - createOrUpdateOrgSecret: ["PUT /orgs/{org}/actions/secrets/{secret_name}"], - createOrUpdateRepoSecret: ["PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}"], - createRegistrationTokenForOrg: ["POST /orgs/{org}/actions/runners/registration-token"], - createRegistrationTokenForRepo: ["POST /repos/{owner}/{repo}/actions/runners/registration-token"], - createRemoveTokenForOrg: ["POST /orgs/{org}/actions/runners/remove-token"], - createRemoveTokenForRepo: ["POST /repos/{owner}/{repo}/actions/runners/remove-token"], - createWorkflowDispatch: ["POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches"], - deleteActionsCacheById: ["DELETE /repos/{owner}/{repo}/actions/caches/{cache_id}"], - deleteActionsCacheByKey: ["DELETE /repos/{owner}/{repo}/actions/caches{?key,ref}"], - deleteArtifact: ["DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"], - deleteEnvironmentSecret: ["DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"], - deleteOrgSecret: ["DELETE /orgs/{org}/actions/secrets/{secret_name}"], - deleteRepoSecret: ["DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}"], - deleteSelfHostedRunnerFromOrg: ["DELETE /orgs/{org}/actions/runners/{runner_id}"], - deleteSelfHostedRunnerFromRepo: ["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}"], - deleteWorkflowRun: ["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}"], - deleteWorkflowRunLogs: ["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs"], - disableSelectedRepositoryGithubActionsOrganization: ["DELETE /orgs/{org}/actions/permissions/repositories/{repository_id}"], - disableWorkflow: ["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable"], - downloadArtifact: ["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}"], - downloadJobLogsForWorkflowRun: ["GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs"], - downloadWorkflowRunAttemptLogs: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs"], - downloadWorkflowRunLogs: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs"], - enableSelectedRepositoryGithubActionsOrganization: ["PUT /orgs/{org}/actions/permissions/repositories/{repository_id}"], - enableWorkflow: ["PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable"], - getActionsCacheList: ["GET /repos/{owner}/{repo}/actions/caches"], - getActionsCacheUsage: ["GET /repos/{owner}/{repo}/actions/cache/usage"], - getActionsCacheUsageByRepoForOrg: ["GET /orgs/{org}/actions/cache/usage-by-repository"], - getActionsCacheUsageForEnterprise: ["GET /enterprises/{enterprise}/actions/cache/usage"], - getActionsCacheUsageForOrg: ["GET /orgs/{org}/actions/cache/usage"], - getAllowedActionsOrganization: ["GET /orgs/{org}/actions/permissions/selected-actions"], - getAllowedActionsRepository: ["GET /repos/{owner}/{repo}/actions/permissions/selected-actions"], - getArtifact: ["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"], - getEnvironmentPublicKey: ["GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key"], - getEnvironmentSecret: ["GET /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}"], - getGithubActionsDefaultWorkflowPermissionsEnterprise: ["GET /enterprises/{enterprise}/actions/permissions/workflow"], - getGithubActionsDefaultWorkflowPermissionsOrganization: ["GET /orgs/{org}/actions/permissions/workflow"], - getGithubActionsDefaultWorkflowPermissionsRepository: ["GET /repos/{owner}/{repo}/actions/permissions/workflow"], - getGithubActionsPermissionsOrganization: ["GET /orgs/{org}/actions/permissions"], - getGithubActionsPermissionsRepository: ["GET /repos/{owner}/{repo}/actions/permissions"], - getJobForWorkflowRun: ["GET /repos/{owner}/{repo}/actions/jobs/{job_id}"], - getOrgPublicKey: ["GET /orgs/{org}/actions/secrets/public-key"], - getOrgSecret: ["GET /orgs/{org}/actions/secrets/{secret_name}"], - getPendingDeploymentsForRun: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"], - getRepoPermissions: ["GET /repos/{owner}/{repo}/actions/permissions", {}, { - renamed: ["actions", "getGithubActionsPermissionsRepository"] - }], - getRepoPublicKey: ["GET /repos/{owner}/{repo}/actions/secrets/public-key"], - getRepoSecret: ["GET /repos/{owner}/{repo}/actions/secrets/{secret_name}"], - getReviewsForRun: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/approvals"], - getSelfHostedRunnerForOrg: ["GET /orgs/{org}/actions/runners/{runner_id}"], - getSelfHostedRunnerForRepo: ["GET /repos/{owner}/{repo}/actions/runners/{runner_id}"], - getWorkflow: ["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}"], - getWorkflowAccessToRepository: ["GET /repos/{owner}/{repo}/actions/permissions/access"], - getWorkflowRun: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}"], - getWorkflowRunAttempt: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}"], - getWorkflowRunUsage: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing"], - getWorkflowUsage: ["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing"], - listArtifactsForRepo: ["GET /repos/{owner}/{repo}/actions/artifacts"], - listEnvironmentSecrets: ["GET /repositories/{repository_id}/environments/{environment_name}/secrets"], - listJobsForWorkflowRun: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs"], - listJobsForWorkflowRunAttempt: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs"], - listLabelsForSelfHostedRunnerForOrg: ["GET /orgs/{org}/actions/runners/{runner_id}/labels"], - listLabelsForSelfHostedRunnerForRepo: ["GET /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"], - listOrgSecrets: ["GET /orgs/{org}/actions/secrets"], - listRepoSecrets: ["GET /repos/{owner}/{repo}/actions/secrets"], - listRepoWorkflows: ["GET /repos/{owner}/{repo}/actions/workflows"], - listRunnerApplicationsForOrg: ["GET /orgs/{org}/actions/runners/downloads"], - listRunnerApplicationsForRepo: ["GET /repos/{owner}/{repo}/actions/runners/downloads"], - listSelectedReposForOrgSecret: ["GET /orgs/{org}/actions/secrets/{secret_name}/repositories"], - listSelectedRepositoriesEnabledGithubActionsOrganization: ["GET /orgs/{org}/actions/permissions/repositories"], - listSelfHostedRunnersForOrg: ["GET /orgs/{org}/actions/runners"], - listSelfHostedRunnersForRepo: ["GET /repos/{owner}/{repo}/actions/runners"], - listWorkflowRunArtifacts: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts"], - listWorkflowRuns: ["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs"], - listWorkflowRunsForRepo: ["GET /repos/{owner}/{repo}/actions/runs"], - reRunJobForWorkflowRun: ["POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun"], - reRunWorkflow: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun"], - reRunWorkflowFailedJobs: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs"], - removeAllCustomLabelsFromSelfHostedRunnerForOrg: ["DELETE /orgs/{org}/actions/runners/{runner_id}/labels"], - removeAllCustomLabelsFromSelfHostedRunnerForRepo: ["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"], - removeCustomLabelFromSelfHostedRunnerForOrg: ["DELETE /orgs/{org}/actions/runners/{runner_id}/labels/{name}"], - removeCustomLabelFromSelfHostedRunnerForRepo: ["DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels/{name}"], - removeSelectedRepoFromOrgSecret: ["DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"], - reviewPendingDeploymentsForRun: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments"], - setAllowedActionsOrganization: ["PUT /orgs/{org}/actions/permissions/selected-actions"], - setAllowedActionsRepository: ["PUT /repos/{owner}/{repo}/actions/permissions/selected-actions"], - setCustomLabelsForSelfHostedRunnerForOrg: ["PUT /orgs/{org}/actions/runners/{runner_id}/labels"], - setCustomLabelsForSelfHostedRunnerForRepo: ["PUT /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"], - setGithubActionsDefaultWorkflowPermissionsEnterprise: ["PUT /enterprises/{enterprise}/actions/permissions/workflow"], - setGithubActionsDefaultWorkflowPermissionsOrganization: ["PUT /orgs/{org}/actions/permissions/workflow"], - setGithubActionsDefaultWorkflowPermissionsRepository: ["PUT /repos/{owner}/{repo}/actions/permissions/workflow"], - setGithubActionsPermissionsOrganization: ["PUT /orgs/{org}/actions/permissions"], - setGithubActionsPermissionsRepository: ["PUT /repos/{owner}/{repo}/actions/permissions"], - setSelectedReposForOrgSecret: ["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories"], - setSelectedRepositoriesEnabledGithubActionsOrganization: ["PUT /orgs/{org}/actions/permissions/repositories"], - setWorkflowAccessToRepository: ["PUT /repos/{owner}/{repo}/actions/permissions/access"] - }, - activity: { - checkRepoIsStarredByAuthenticatedUser: ["GET /user/starred/{owner}/{repo}"], - deleteRepoSubscription: ["DELETE /repos/{owner}/{repo}/subscription"], - deleteThreadSubscription: ["DELETE /notifications/threads/{thread_id}/subscription"], - getFeeds: ["GET /feeds"], - getRepoSubscription: ["GET /repos/{owner}/{repo}/subscription"], - getThread: ["GET /notifications/threads/{thread_id}"], - getThreadSubscriptionForAuthenticatedUser: ["GET /notifications/threads/{thread_id}/subscription"], - listEventsForAuthenticatedUser: ["GET /users/{username}/events"], - listNotificationsForAuthenticatedUser: ["GET /notifications"], - listOrgEventsForAuthenticatedUser: ["GET /users/{username}/events/orgs/{org}"], - listPublicEvents: ["GET /events"], - listPublicEventsForRepoNetwork: ["GET /networks/{owner}/{repo}/events"], - listPublicEventsForUser: ["GET /users/{username}/events/public"], - listPublicOrgEvents: ["GET /orgs/{org}/events"], - listReceivedEventsForUser: ["GET /users/{username}/received_events"], - listReceivedPublicEventsForUser: ["GET /users/{username}/received_events/public"], - listRepoEvents: ["GET /repos/{owner}/{repo}/events"], - listRepoNotificationsForAuthenticatedUser: ["GET /repos/{owner}/{repo}/notifications"], - listReposStarredByAuthenticatedUser: ["GET /user/starred"], - listReposStarredByUser: ["GET /users/{username}/starred"], - listReposWatchedByUser: ["GET /users/{username}/subscriptions"], - listStargazersForRepo: ["GET /repos/{owner}/{repo}/stargazers"], - listWatchedReposForAuthenticatedUser: ["GET /user/subscriptions"], - listWatchersForRepo: ["GET /repos/{owner}/{repo}/subscribers"], - markNotificationsAsRead: ["PUT /notifications"], - markRepoNotificationsAsRead: ["PUT /repos/{owner}/{repo}/notifications"], - markThreadAsRead: ["PATCH /notifications/threads/{thread_id}"], - setRepoSubscription: ["PUT /repos/{owner}/{repo}/subscription"], - setThreadSubscription: ["PUT /notifications/threads/{thread_id}/subscription"], - starRepoForAuthenticatedUser: ["PUT /user/starred/{owner}/{repo}"], - unstarRepoForAuthenticatedUser: ["DELETE /user/starred/{owner}/{repo}"] - }, - apps: { - addRepoToInstallation: ["PUT /user/installations/{installation_id}/repositories/{repository_id}", {}, { - renamed: ["apps", "addRepoToInstallationForAuthenticatedUser"] - }], - addRepoToInstallationForAuthenticatedUser: ["PUT /user/installations/{installation_id}/repositories/{repository_id}"], - checkToken: ["POST /applications/{client_id}/token"], - createFromManifest: ["POST /app-manifests/{code}/conversions"], - createInstallationAccessToken: ["POST /app/installations/{installation_id}/access_tokens"], - deleteAuthorization: ["DELETE /applications/{client_id}/grant"], - deleteInstallation: ["DELETE /app/installations/{installation_id}"], - deleteToken: ["DELETE /applications/{client_id}/token"], - getAuthenticated: ["GET /app"], - getBySlug: ["GET /apps/{app_slug}"], - getInstallation: ["GET /app/installations/{installation_id}"], - getOrgInstallation: ["GET /orgs/{org}/installation"], - getRepoInstallation: ["GET /repos/{owner}/{repo}/installation"], - getSubscriptionPlanForAccount: ["GET /marketplace_listing/accounts/{account_id}"], - getSubscriptionPlanForAccountStubbed: ["GET /marketplace_listing/stubbed/accounts/{account_id}"], - getUserInstallation: ["GET /users/{username}/installation"], - getWebhookConfigForApp: ["GET /app/hook/config"], - getWebhookDelivery: ["GET /app/hook/deliveries/{delivery_id}"], - listAccountsForPlan: ["GET /marketplace_listing/plans/{plan_id}/accounts"], - listAccountsForPlanStubbed: ["GET /marketplace_listing/stubbed/plans/{plan_id}/accounts"], - listInstallationReposForAuthenticatedUser: ["GET /user/installations/{installation_id}/repositories"], - listInstallations: ["GET /app/installations"], - listInstallationsForAuthenticatedUser: ["GET /user/installations"], - listPlans: ["GET /marketplace_listing/plans"], - listPlansStubbed: ["GET /marketplace_listing/stubbed/plans"], - listReposAccessibleToInstallation: ["GET /installation/repositories"], - listSubscriptionsForAuthenticatedUser: ["GET /user/marketplace_purchases"], - listSubscriptionsForAuthenticatedUserStubbed: ["GET /user/marketplace_purchases/stubbed"], - listWebhookDeliveries: ["GET /app/hook/deliveries"], - redeliverWebhookDelivery: ["POST /app/hook/deliveries/{delivery_id}/attempts"], - removeRepoFromInstallation: ["DELETE /user/installations/{installation_id}/repositories/{repository_id}", {}, { - renamed: ["apps", "removeRepoFromInstallationForAuthenticatedUser"] - }], - removeRepoFromInstallationForAuthenticatedUser: ["DELETE /user/installations/{installation_id}/repositories/{repository_id}"], - resetToken: ["PATCH /applications/{client_id}/token"], - revokeInstallationAccessToken: ["DELETE /installation/token"], - scopeToken: ["POST /applications/{client_id}/token/scoped"], - suspendInstallation: ["PUT /app/installations/{installation_id}/suspended"], - unsuspendInstallation: ["DELETE /app/installations/{installation_id}/suspended"], - updateWebhookConfigForApp: ["PATCH /app/hook/config"] - }, - billing: { - getGithubActionsBillingOrg: ["GET /orgs/{org}/settings/billing/actions"], - getGithubActionsBillingUser: ["GET /users/{username}/settings/billing/actions"], - getGithubAdvancedSecurityBillingGhe: ["GET /enterprises/{enterprise}/settings/billing/advanced-security"], - getGithubAdvancedSecurityBillingOrg: ["GET /orgs/{org}/settings/billing/advanced-security"], - getGithubPackagesBillingOrg: ["GET /orgs/{org}/settings/billing/packages"], - getGithubPackagesBillingUser: ["GET /users/{username}/settings/billing/packages"], - getSharedStorageBillingOrg: ["GET /orgs/{org}/settings/billing/shared-storage"], - getSharedStorageBillingUser: ["GET /users/{username}/settings/billing/shared-storage"] - }, - checks: { - create: ["POST /repos/{owner}/{repo}/check-runs"], - createSuite: ["POST /repos/{owner}/{repo}/check-suites"], - get: ["GET /repos/{owner}/{repo}/check-runs/{check_run_id}"], - getSuite: ["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}"], - listAnnotations: ["GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations"], - listForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/check-runs"], - listForSuite: ["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs"], - listSuitesForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/check-suites"], - rerequestRun: ["POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest"], - rerequestSuite: ["POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest"], - setSuitesPreferences: ["PATCH /repos/{owner}/{repo}/check-suites/preferences"], - update: ["PATCH /repos/{owner}/{repo}/check-runs/{check_run_id}"] - }, - codeScanning: { - deleteAnalysis: ["DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}{?confirm_delete}"], - getAlert: ["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}", {}, { - renamedParameters: { - alert_id: "alert_number" - } - }], - getAnalysis: ["GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}"], - getSarif: ["GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}"], - listAlertInstances: ["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances"], - listAlertsForOrg: ["GET /orgs/{org}/code-scanning/alerts"], - listAlertsForRepo: ["GET /repos/{owner}/{repo}/code-scanning/alerts"], - listAlertsInstances: ["GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances", {}, { - renamed: ["codeScanning", "listAlertInstances"] - }], - listRecentAnalyses: ["GET /repos/{owner}/{repo}/code-scanning/analyses"], - updateAlert: ["PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}"], - uploadSarif: ["POST /repos/{owner}/{repo}/code-scanning/sarifs"] - }, - codesOfConduct: { - getAllCodesOfConduct: ["GET /codes_of_conduct"], - getConductCode: ["GET /codes_of_conduct/{key}"] - }, - codespaces: { - addRepositoryForSecretForAuthenticatedUser: ["PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"], - codespaceMachinesForAuthenticatedUser: ["GET /user/codespaces/{codespace_name}/machines"], - createForAuthenticatedUser: ["POST /user/codespaces"], - createOrUpdateRepoSecret: ["PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"], - createOrUpdateSecretForAuthenticatedUser: ["PUT /user/codespaces/secrets/{secret_name}"], - createWithPrForAuthenticatedUser: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces"], - createWithRepoForAuthenticatedUser: ["POST /repos/{owner}/{repo}/codespaces"], - deleteForAuthenticatedUser: ["DELETE /user/codespaces/{codespace_name}"], - deleteFromOrganization: ["DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name}"], - deleteRepoSecret: ["DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"], - deleteSecretForAuthenticatedUser: ["DELETE /user/codespaces/secrets/{secret_name}"], - exportForAuthenticatedUser: ["POST /user/codespaces/{codespace_name}/exports"], - getExportDetailsForAuthenticatedUser: ["GET /user/codespaces/{codespace_name}/exports/{export_id}"], - getForAuthenticatedUser: ["GET /user/codespaces/{codespace_name}"], - getPublicKeyForAuthenticatedUser: ["GET /user/codespaces/secrets/public-key"], - getRepoPublicKey: ["GET /repos/{owner}/{repo}/codespaces/secrets/public-key"], - getRepoSecret: ["GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name}"], - getSecretForAuthenticatedUser: ["GET /user/codespaces/secrets/{secret_name}"], - listDevcontainersInRepositoryForAuthenticatedUser: ["GET /repos/{owner}/{repo}/codespaces/devcontainers"], - listForAuthenticatedUser: ["GET /user/codespaces"], - listInOrganization: ["GET /orgs/{org}/codespaces", {}, { - renamedParameters: { - org_id: "org" - } - }], - listInRepositoryForAuthenticatedUser: ["GET /repos/{owner}/{repo}/codespaces"], - listRepoSecrets: ["GET /repos/{owner}/{repo}/codespaces/secrets"], - listRepositoriesForSecretForAuthenticatedUser: ["GET /user/codespaces/secrets/{secret_name}/repositories"], - listSecretsForAuthenticatedUser: ["GET /user/codespaces/secrets"], - removeRepositoryForSecretForAuthenticatedUser: ["DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id}"], - repoMachinesForAuthenticatedUser: ["GET /repos/{owner}/{repo}/codespaces/machines"], - setRepositoriesForSecretForAuthenticatedUser: ["PUT /user/codespaces/secrets/{secret_name}/repositories"], - startForAuthenticatedUser: ["POST /user/codespaces/{codespace_name}/start"], - stopForAuthenticatedUser: ["POST /user/codespaces/{codespace_name}/stop"], - stopInOrganization: ["POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop"], - updateForAuthenticatedUser: ["PATCH /user/codespaces/{codespace_name}"] - }, - dependabot: { - addSelectedRepoToOrgSecret: ["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"], - createOrUpdateOrgSecret: ["PUT /orgs/{org}/dependabot/secrets/{secret_name}"], - createOrUpdateRepoSecret: ["PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"], - deleteOrgSecret: ["DELETE /orgs/{org}/dependabot/secrets/{secret_name}"], - deleteRepoSecret: ["DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"], - getOrgPublicKey: ["GET /orgs/{org}/dependabot/secrets/public-key"], - getOrgSecret: ["GET /orgs/{org}/dependabot/secrets/{secret_name}"], - getRepoPublicKey: ["GET /repos/{owner}/{repo}/dependabot/secrets/public-key"], - getRepoSecret: ["GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name}"], - listOrgSecrets: ["GET /orgs/{org}/dependabot/secrets"], - listRepoSecrets: ["GET /repos/{owner}/{repo}/dependabot/secrets"], - listSelectedReposForOrgSecret: ["GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories"], - removeSelectedRepoFromOrgSecret: ["DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}"], - setSelectedReposForOrgSecret: ["PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories"] - }, - dependencyGraph: { - createRepositorySnapshot: ["POST /repos/{owner}/{repo}/dependency-graph/snapshots"], - diffRange: ["GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}"] - }, - emojis: { - get: ["GET /emojis"] - }, - enterpriseAdmin: { - addCustomLabelsToSelfHostedRunnerForEnterprise: ["POST /enterprises/{enterprise}/actions/runners/{runner_id}/labels"], - disableSelectedOrganizationGithubActionsEnterprise: ["DELETE /enterprises/{enterprise}/actions/permissions/organizations/{org_id}"], - enableSelectedOrganizationGithubActionsEnterprise: ["PUT /enterprises/{enterprise}/actions/permissions/organizations/{org_id}"], - getAllowedActionsEnterprise: ["GET /enterprises/{enterprise}/actions/permissions/selected-actions"], - getGithubActionsPermissionsEnterprise: ["GET /enterprises/{enterprise}/actions/permissions"], - getServerStatistics: ["GET /enterprise-installation/{enterprise_or_org}/server-statistics"], - listLabelsForSelfHostedRunnerForEnterprise: ["GET /enterprises/{enterprise}/actions/runners/{runner_id}/labels"], - listSelectedOrganizationsEnabledGithubActionsEnterprise: ["GET /enterprises/{enterprise}/actions/permissions/organizations"], - removeAllCustomLabelsFromSelfHostedRunnerForEnterprise: ["DELETE /enterprises/{enterprise}/actions/runners/{runner_id}/labels"], - removeCustomLabelFromSelfHostedRunnerForEnterprise: ["DELETE /enterprises/{enterprise}/actions/runners/{runner_id}/labels/{name}"], - setAllowedActionsEnterprise: ["PUT /enterprises/{enterprise}/actions/permissions/selected-actions"], - setCustomLabelsForSelfHostedRunnerForEnterprise: ["PUT /enterprises/{enterprise}/actions/runners/{runner_id}/labels"], - setGithubActionsPermissionsEnterprise: ["PUT /enterprises/{enterprise}/actions/permissions"], - setSelectedOrganizationsEnabledGithubActionsEnterprise: ["PUT /enterprises/{enterprise}/actions/permissions/organizations"] - }, - gists: { - checkIsStarred: ["GET /gists/{gist_id}/star"], - create: ["POST /gists"], - createComment: ["POST /gists/{gist_id}/comments"], - delete: ["DELETE /gists/{gist_id}"], - deleteComment: ["DELETE /gists/{gist_id}/comments/{comment_id}"], - fork: ["POST /gists/{gist_id}/forks"], - get: ["GET /gists/{gist_id}"], - getComment: ["GET /gists/{gist_id}/comments/{comment_id}"], - getRevision: ["GET /gists/{gist_id}/{sha}"], - list: ["GET /gists"], - listComments: ["GET /gists/{gist_id}/comments"], - listCommits: ["GET /gists/{gist_id}/commits"], - listForUser: ["GET /users/{username}/gists"], - listForks: ["GET /gists/{gist_id}/forks"], - listPublic: ["GET /gists/public"], - listStarred: ["GET /gists/starred"], - star: ["PUT /gists/{gist_id}/star"], - unstar: ["DELETE /gists/{gist_id}/star"], - update: ["PATCH /gists/{gist_id}"], - updateComment: ["PATCH /gists/{gist_id}/comments/{comment_id}"] - }, - git: { - createBlob: ["POST /repos/{owner}/{repo}/git/blobs"], - createCommit: ["POST /repos/{owner}/{repo}/git/commits"], - createRef: ["POST /repos/{owner}/{repo}/git/refs"], - createTag: ["POST /repos/{owner}/{repo}/git/tags"], - createTree: ["POST /repos/{owner}/{repo}/git/trees"], - deleteRef: ["DELETE /repos/{owner}/{repo}/git/refs/{ref}"], - getBlob: ["GET /repos/{owner}/{repo}/git/blobs/{file_sha}"], - getCommit: ["GET /repos/{owner}/{repo}/git/commits/{commit_sha}"], - getRef: ["GET /repos/{owner}/{repo}/git/ref/{ref}"], - getTag: ["GET /repos/{owner}/{repo}/git/tags/{tag_sha}"], - getTree: ["GET /repos/{owner}/{repo}/git/trees/{tree_sha}"], - listMatchingRefs: ["GET /repos/{owner}/{repo}/git/matching-refs/{ref}"], - updateRef: ["PATCH /repos/{owner}/{repo}/git/refs/{ref}"] - }, - gitignore: { - getAllTemplates: ["GET /gitignore/templates"], - getTemplate: ["GET /gitignore/templates/{name}"] - }, - interactions: { - getRestrictionsForAuthenticatedUser: ["GET /user/interaction-limits"], - getRestrictionsForOrg: ["GET /orgs/{org}/interaction-limits"], - getRestrictionsForRepo: ["GET /repos/{owner}/{repo}/interaction-limits"], - getRestrictionsForYourPublicRepos: ["GET /user/interaction-limits", {}, { - renamed: ["interactions", "getRestrictionsForAuthenticatedUser"] - }], - removeRestrictionsForAuthenticatedUser: ["DELETE /user/interaction-limits"], - removeRestrictionsForOrg: ["DELETE /orgs/{org}/interaction-limits"], - removeRestrictionsForRepo: ["DELETE /repos/{owner}/{repo}/interaction-limits"], - removeRestrictionsForYourPublicRepos: ["DELETE /user/interaction-limits", {}, { - renamed: ["interactions", "removeRestrictionsForAuthenticatedUser"] - }], - setRestrictionsForAuthenticatedUser: ["PUT /user/interaction-limits"], - setRestrictionsForOrg: ["PUT /orgs/{org}/interaction-limits"], - setRestrictionsForRepo: ["PUT /repos/{owner}/{repo}/interaction-limits"], - setRestrictionsForYourPublicRepos: ["PUT /user/interaction-limits", {}, { - renamed: ["interactions", "setRestrictionsForAuthenticatedUser"] - }] - }, - issues: { - addAssignees: ["POST /repos/{owner}/{repo}/issues/{issue_number}/assignees"], - addLabels: ["POST /repos/{owner}/{repo}/issues/{issue_number}/labels"], - checkUserCanBeAssigned: ["GET /repos/{owner}/{repo}/assignees/{assignee}"], - create: ["POST /repos/{owner}/{repo}/issues"], - createComment: ["POST /repos/{owner}/{repo}/issues/{issue_number}/comments"], - createLabel: ["POST /repos/{owner}/{repo}/labels"], - createMilestone: ["POST /repos/{owner}/{repo}/milestones"], - deleteComment: ["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}"], - deleteLabel: ["DELETE /repos/{owner}/{repo}/labels/{name}"], - deleteMilestone: ["DELETE /repos/{owner}/{repo}/milestones/{milestone_number}"], - get: ["GET /repos/{owner}/{repo}/issues/{issue_number}"], - getComment: ["GET /repos/{owner}/{repo}/issues/comments/{comment_id}"], - getEvent: ["GET /repos/{owner}/{repo}/issues/events/{event_id}"], - getLabel: ["GET /repos/{owner}/{repo}/labels/{name}"], - getMilestone: ["GET /repos/{owner}/{repo}/milestones/{milestone_number}"], - list: ["GET /issues"], - listAssignees: ["GET /repos/{owner}/{repo}/assignees"], - listComments: ["GET /repos/{owner}/{repo}/issues/{issue_number}/comments"], - listCommentsForRepo: ["GET /repos/{owner}/{repo}/issues/comments"], - listEvents: ["GET /repos/{owner}/{repo}/issues/{issue_number}/events"], - listEventsForRepo: ["GET /repos/{owner}/{repo}/issues/events"], - listEventsForTimeline: ["GET /repos/{owner}/{repo}/issues/{issue_number}/timeline"], - listForAuthenticatedUser: ["GET /user/issues"], - listForOrg: ["GET /orgs/{org}/issues"], - listForRepo: ["GET /repos/{owner}/{repo}/issues"], - listLabelsForMilestone: ["GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels"], - listLabelsForRepo: ["GET /repos/{owner}/{repo}/labels"], - listLabelsOnIssue: ["GET /repos/{owner}/{repo}/issues/{issue_number}/labels"], - listMilestones: ["GET /repos/{owner}/{repo}/milestones"], - lock: ["PUT /repos/{owner}/{repo}/issues/{issue_number}/lock"], - removeAllLabels: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels"], - removeAssignees: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees"], - removeLabel: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}"], - setLabels: ["PUT /repos/{owner}/{repo}/issues/{issue_number}/labels"], - unlock: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock"], - update: ["PATCH /repos/{owner}/{repo}/issues/{issue_number}"], - updateComment: ["PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}"], - updateLabel: ["PATCH /repos/{owner}/{repo}/labels/{name}"], - updateMilestone: ["PATCH /repos/{owner}/{repo}/milestones/{milestone_number}"] - }, - licenses: { - get: ["GET /licenses/{license}"], - getAllCommonlyUsed: ["GET /licenses"], - getForRepo: ["GET /repos/{owner}/{repo}/license"] - }, - markdown: { - render: ["POST /markdown"], - renderRaw: ["POST /markdown/raw", { - headers: { - "content-type": "text/plain; charset=utf-8" - } - }] - }, - meta: { - get: ["GET /meta"], - getOctocat: ["GET /octocat"], - getZen: ["GET /zen"], - root: ["GET /"] - }, - migrations: { - cancelImport: ["DELETE /repos/{owner}/{repo}/import"], - deleteArchiveForAuthenticatedUser: ["DELETE /user/migrations/{migration_id}/archive"], - deleteArchiveForOrg: ["DELETE /orgs/{org}/migrations/{migration_id}/archive"], - downloadArchiveForOrg: ["GET /orgs/{org}/migrations/{migration_id}/archive"], - getArchiveForAuthenticatedUser: ["GET /user/migrations/{migration_id}/archive"], - getCommitAuthors: ["GET /repos/{owner}/{repo}/import/authors"], - getImportStatus: ["GET /repos/{owner}/{repo}/import"], - getLargeFiles: ["GET /repos/{owner}/{repo}/import/large_files"], - getStatusForAuthenticatedUser: ["GET /user/migrations/{migration_id}"], - getStatusForOrg: ["GET /orgs/{org}/migrations/{migration_id}"], - listForAuthenticatedUser: ["GET /user/migrations"], - listForOrg: ["GET /orgs/{org}/migrations"], - listReposForAuthenticatedUser: ["GET /user/migrations/{migration_id}/repositories"], - listReposForOrg: ["GET /orgs/{org}/migrations/{migration_id}/repositories"], - listReposForUser: ["GET /user/migrations/{migration_id}/repositories", {}, { - renamed: ["migrations", "listReposForAuthenticatedUser"] - }], - mapCommitAuthor: ["PATCH /repos/{owner}/{repo}/import/authors/{author_id}"], - setLfsPreference: ["PATCH /repos/{owner}/{repo}/import/lfs"], - startForAuthenticatedUser: ["POST /user/migrations"], - startForOrg: ["POST /orgs/{org}/migrations"], - startImport: ["PUT /repos/{owner}/{repo}/import"], - unlockRepoForAuthenticatedUser: ["DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock"], - unlockRepoForOrg: ["DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock"], - updateImport: ["PATCH /repos/{owner}/{repo}/import"] - }, - orgs: { - blockUser: ["PUT /orgs/{org}/blocks/{username}"], - cancelInvitation: ["DELETE /orgs/{org}/invitations/{invitation_id}"], - checkBlockedUser: ["GET /orgs/{org}/blocks/{username}"], - checkMembershipForUser: ["GET /orgs/{org}/members/{username}"], - checkPublicMembershipForUser: ["GET /orgs/{org}/public_members/{username}"], - convertMemberToOutsideCollaborator: ["PUT /orgs/{org}/outside_collaborators/{username}"], - createInvitation: ["POST /orgs/{org}/invitations"], - createWebhook: ["POST /orgs/{org}/hooks"], - deleteWebhook: ["DELETE /orgs/{org}/hooks/{hook_id}"], - get: ["GET /orgs/{org}"], - getMembershipForAuthenticatedUser: ["GET /user/memberships/orgs/{org}"], - getMembershipForUser: ["GET /orgs/{org}/memberships/{username}"], - getWebhook: ["GET /orgs/{org}/hooks/{hook_id}"], - getWebhookConfigForOrg: ["GET /orgs/{org}/hooks/{hook_id}/config"], - getWebhookDelivery: ["GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}"], - list: ["GET /organizations"], - listAppInstallations: ["GET /orgs/{org}/installations"], - listBlockedUsers: ["GET /orgs/{org}/blocks"], - listCustomRoles: ["GET /organizations/{organization_id}/custom_roles"], - listFailedInvitations: ["GET /orgs/{org}/failed_invitations"], - listForAuthenticatedUser: ["GET /user/orgs"], - listForUser: ["GET /users/{username}/orgs"], - listInvitationTeams: ["GET /orgs/{org}/invitations/{invitation_id}/teams"], - listMembers: ["GET /orgs/{org}/members"], - listMembershipsForAuthenticatedUser: ["GET /user/memberships/orgs"], - listOutsideCollaborators: ["GET /orgs/{org}/outside_collaborators"], - listPendingInvitations: ["GET /orgs/{org}/invitations"], - listPublicMembers: ["GET /orgs/{org}/public_members"], - listWebhookDeliveries: ["GET /orgs/{org}/hooks/{hook_id}/deliveries"], - listWebhooks: ["GET /orgs/{org}/hooks"], - pingWebhook: ["POST /orgs/{org}/hooks/{hook_id}/pings"], - redeliverWebhookDelivery: ["POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"], - removeMember: ["DELETE /orgs/{org}/members/{username}"], - removeMembershipForUser: ["DELETE /orgs/{org}/memberships/{username}"], - removeOutsideCollaborator: ["DELETE /orgs/{org}/outside_collaborators/{username}"], - removePublicMembershipForAuthenticatedUser: ["DELETE /orgs/{org}/public_members/{username}"], - setMembershipForUser: ["PUT /orgs/{org}/memberships/{username}"], - setPublicMembershipForAuthenticatedUser: ["PUT /orgs/{org}/public_members/{username}"], - unblockUser: ["DELETE /orgs/{org}/blocks/{username}"], - update: ["PATCH /orgs/{org}"], - updateMembershipForAuthenticatedUser: ["PATCH /user/memberships/orgs/{org}"], - updateWebhook: ["PATCH /orgs/{org}/hooks/{hook_id}"], - updateWebhookConfigForOrg: ["PATCH /orgs/{org}/hooks/{hook_id}/config"] - }, - packages: { - deletePackageForAuthenticatedUser: ["DELETE /user/packages/{package_type}/{package_name}"], - deletePackageForOrg: ["DELETE /orgs/{org}/packages/{package_type}/{package_name}"], - deletePackageForUser: ["DELETE /users/{username}/packages/{package_type}/{package_name}"], - deletePackageVersionForAuthenticatedUser: ["DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id}"], - deletePackageVersionForOrg: ["DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"], - deletePackageVersionForUser: ["DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"], - getAllPackageVersionsForAPackageOwnedByAnOrg: ["GET /orgs/{org}/packages/{package_type}/{package_name}/versions", {}, { - renamed: ["packages", "getAllPackageVersionsForPackageOwnedByOrg"] - }], - getAllPackageVersionsForAPackageOwnedByTheAuthenticatedUser: ["GET /user/packages/{package_type}/{package_name}/versions", {}, { - renamed: ["packages", "getAllPackageVersionsForPackageOwnedByAuthenticatedUser"] - }], - getAllPackageVersionsForPackageOwnedByAuthenticatedUser: ["GET /user/packages/{package_type}/{package_name}/versions"], - getAllPackageVersionsForPackageOwnedByOrg: ["GET /orgs/{org}/packages/{package_type}/{package_name}/versions"], - getAllPackageVersionsForPackageOwnedByUser: ["GET /users/{username}/packages/{package_type}/{package_name}/versions"], - getPackageForAuthenticatedUser: ["GET /user/packages/{package_type}/{package_name}"], - getPackageForOrganization: ["GET /orgs/{org}/packages/{package_type}/{package_name}"], - getPackageForUser: ["GET /users/{username}/packages/{package_type}/{package_name}"], - getPackageVersionForAuthenticatedUser: ["GET /user/packages/{package_type}/{package_name}/versions/{package_version_id}"], - getPackageVersionForOrganization: ["GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}"], - getPackageVersionForUser: ["GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}"], - listPackagesForAuthenticatedUser: ["GET /user/packages"], - listPackagesForOrganization: ["GET /orgs/{org}/packages"], - listPackagesForUser: ["GET /users/{username}/packages"], - restorePackageForAuthenticatedUser: ["POST /user/packages/{package_type}/{package_name}/restore{?token}"], - restorePackageForOrg: ["POST /orgs/{org}/packages/{package_type}/{package_name}/restore{?token}"], - restorePackageForUser: ["POST /users/{username}/packages/{package_type}/{package_name}/restore{?token}"], - restorePackageVersionForAuthenticatedUser: ["POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"], - restorePackageVersionForOrg: ["POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"], - restorePackageVersionForUser: ["POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore"] - }, - projects: { - addCollaborator: ["PUT /projects/{project_id}/collaborators/{username}"], - createCard: ["POST /projects/columns/{column_id}/cards"], - createColumn: ["POST /projects/{project_id}/columns"], - createForAuthenticatedUser: ["POST /user/projects"], - createForOrg: ["POST /orgs/{org}/projects"], - createForRepo: ["POST /repos/{owner}/{repo}/projects"], - delete: ["DELETE /projects/{project_id}"], - deleteCard: ["DELETE /projects/columns/cards/{card_id}"], - deleteColumn: ["DELETE /projects/columns/{column_id}"], - get: ["GET /projects/{project_id}"], - getCard: ["GET /projects/columns/cards/{card_id}"], - getColumn: ["GET /projects/columns/{column_id}"], - getPermissionForUser: ["GET /projects/{project_id}/collaborators/{username}/permission"], - listCards: ["GET /projects/columns/{column_id}/cards"], - listCollaborators: ["GET /projects/{project_id}/collaborators"], - listColumns: ["GET /projects/{project_id}/columns"], - listForOrg: ["GET /orgs/{org}/projects"], - listForRepo: ["GET /repos/{owner}/{repo}/projects"], - listForUser: ["GET /users/{username}/projects"], - moveCard: ["POST /projects/columns/cards/{card_id}/moves"], - moveColumn: ["POST /projects/columns/{column_id}/moves"], - removeCollaborator: ["DELETE /projects/{project_id}/collaborators/{username}"], - update: ["PATCH /projects/{project_id}"], - updateCard: ["PATCH /projects/columns/cards/{card_id}"], - updateColumn: ["PATCH /projects/columns/{column_id}"] - }, - pulls: { - checkIfMerged: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/merge"], - create: ["POST /repos/{owner}/{repo}/pulls"], - createReplyForReviewComment: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies"], - createReview: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews"], - createReviewComment: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/comments"], - deletePendingReview: ["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"], - deleteReviewComment: ["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}"], - dismissReview: ["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals"], - get: ["GET /repos/{owner}/{repo}/pulls/{pull_number}"], - getReview: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"], - getReviewComment: ["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}"], - list: ["GET /repos/{owner}/{repo}/pulls"], - listCommentsForReview: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments"], - listCommits: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/commits"], - listFiles: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/files"], - listRequestedReviewers: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"], - listReviewComments: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/comments"], - listReviewCommentsForRepo: ["GET /repos/{owner}/{repo}/pulls/comments"], - listReviews: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews"], - merge: ["PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge"], - removeRequestedReviewers: ["DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"], - requestReviewers: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers"], - submitReview: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events"], - update: ["PATCH /repos/{owner}/{repo}/pulls/{pull_number}"], - updateBranch: ["PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch"], - updateReview: ["PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}"], - updateReviewComment: ["PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}"] - }, - rateLimit: { - get: ["GET /rate_limit"] - }, - reactions: { - createForCommitComment: ["POST /repos/{owner}/{repo}/comments/{comment_id}/reactions"], - createForIssue: ["POST /repos/{owner}/{repo}/issues/{issue_number}/reactions"], - createForIssueComment: ["POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"], - createForPullRequestReviewComment: ["POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"], - createForRelease: ["POST /repos/{owner}/{repo}/releases/{release_id}/reactions"], - createForTeamDiscussionCommentInOrg: ["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"], - createForTeamDiscussionInOrg: ["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"], - deleteForCommitComment: ["DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id}"], - deleteForIssue: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id}"], - deleteForIssueComment: ["DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id}"], - deleteForPullRequestComment: ["DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id}"], - deleteForRelease: ["DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}"], - deleteForTeamDiscussion: ["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id}"], - deleteForTeamDiscussionComment: ["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id}"], - listForCommitComment: ["GET /repos/{owner}/{repo}/comments/{comment_id}/reactions"], - listForIssue: ["GET /repos/{owner}/{repo}/issues/{issue_number}/reactions"], - listForIssueComment: ["GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions"], - listForPullRequestReviewComment: ["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions"], - listForRelease: ["GET /repos/{owner}/{repo}/releases/{release_id}/reactions"], - listForTeamDiscussionCommentInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions"], - listForTeamDiscussionInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions"] - }, - repos: { - acceptInvitation: ["PATCH /user/repository_invitations/{invitation_id}", {}, { - renamed: ["repos", "acceptInvitationForAuthenticatedUser"] - }], - acceptInvitationForAuthenticatedUser: ["PATCH /user/repository_invitations/{invitation_id}"], - addAppAccessRestrictions: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", {}, { - mapToData: "apps" - }], - addCollaborator: ["PUT /repos/{owner}/{repo}/collaborators/{username}"], - addStatusCheckContexts: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", {}, { - mapToData: "contexts" - }], - addTeamAccessRestrictions: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", {}, { - mapToData: "teams" - }], - addUserAccessRestrictions: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", {}, { - mapToData: "users" - }], - checkCollaborator: ["GET /repos/{owner}/{repo}/collaborators/{username}"], - checkVulnerabilityAlerts: ["GET /repos/{owner}/{repo}/vulnerability-alerts"], - codeownersErrors: ["GET /repos/{owner}/{repo}/codeowners/errors"], - compareCommits: ["GET /repos/{owner}/{repo}/compare/{base}...{head}"], - compareCommitsWithBasehead: ["GET /repos/{owner}/{repo}/compare/{basehead}"], - createAutolink: ["POST /repos/{owner}/{repo}/autolinks"], - createCommitComment: ["POST /repos/{owner}/{repo}/commits/{commit_sha}/comments"], - createCommitSignatureProtection: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"], - createCommitStatus: ["POST /repos/{owner}/{repo}/statuses/{sha}"], - createDeployKey: ["POST /repos/{owner}/{repo}/keys"], - createDeployment: ["POST /repos/{owner}/{repo}/deployments"], - createDeploymentStatus: ["POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"], - createDispatchEvent: ["POST /repos/{owner}/{repo}/dispatches"], - createForAuthenticatedUser: ["POST /user/repos"], - createFork: ["POST /repos/{owner}/{repo}/forks"], - createInOrg: ["POST /orgs/{org}/repos"], - createOrUpdateEnvironment: ["PUT /repos/{owner}/{repo}/environments/{environment_name}"], - createOrUpdateFileContents: ["PUT /repos/{owner}/{repo}/contents/{path}"], - createPagesSite: ["POST /repos/{owner}/{repo}/pages"], - createRelease: ["POST /repos/{owner}/{repo}/releases"], - createTagProtection: ["POST /repos/{owner}/{repo}/tags/protection"], - createUsingTemplate: ["POST /repos/{template_owner}/{template_repo}/generate"], - createWebhook: ["POST /repos/{owner}/{repo}/hooks"], - declineInvitation: ["DELETE /user/repository_invitations/{invitation_id}", {}, { - renamed: ["repos", "declineInvitationForAuthenticatedUser"] - }], - declineInvitationForAuthenticatedUser: ["DELETE /user/repository_invitations/{invitation_id}"], - delete: ["DELETE /repos/{owner}/{repo}"], - deleteAccessRestrictions: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"], - deleteAdminBranchProtection: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"], - deleteAnEnvironment: ["DELETE /repos/{owner}/{repo}/environments/{environment_name}"], - deleteAutolink: ["DELETE /repos/{owner}/{repo}/autolinks/{autolink_id}"], - deleteBranchProtection: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection"], - deleteCommitComment: ["DELETE /repos/{owner}/{repo}/comments/{comment_id}"], - deleteCommitSignatureProtection: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"], - deleteDeployKey: ["DELETE /repos/{owner}/{repo}/keys/{key_id}"], - deleteDeployment: ["DELETE /repos/{owner}/{repo}/deployments/{deployment_id}"], - deleteFile: ["DELETE /repos/{owner}/{repo}/contents/{path}"], - deleteInvitation: ["DELETE /repos/{owner}/{repo}/invitations/{invitation_id}"], - deletePagesSite: ["DELETE /repos/{owner}/{repo}/pages"], - deletePullRequestReviewProtection: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"], - deleteRelease: ["DELETE /repos/{owner}/{repo}/releases/{release_id}"], - deleteReleaseAsset: ["DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}"], - deleteTagProtection: ["DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id}"], - deleteWebhook: ["DELETE /repos/{owner}/{repo}/hooks/{hook_id}"], - disableAutomatedSecurityFixes: ["DELETE /repos/{owner}/{repo}/automated-security-fixes"], - disableLfsForRepo: ["DELETE /repos/{owner}/{repo}/lfs"], - disableVulnerabilityAlerts: ["DELETE /repos/{owner}/{repo}/vulnerability-alerts"], - downloadArchive: ["GET /repos/{owner}/{repo}/zipball/{ref}", {}, { - renamed: ["repos", "downloadZipballArchive"] - }], - downloadTarballArchive: ["GET /repos/{owner}/{repo}/tarball/{ref}"], - downloadZipballArchive: ["GET /repos/{owner}/{repo}/zipball/{ref}"], - enableAutomatedSecurityFixes: ["PUT /repos/{owner}/{repo}/automated-security-fixes"], - enableLfsForRepo: ["PUT /repos/{owner}/{repo}/lfs"], - enableVulnerabilityAlerts: ["PUT /repos/{owner}/{repo}/vulnerability-alerts"], - generateReleaseNotes: ["POST /repos/{owner}/{repo}/releases/generate-notes"], - get: ["GET /repos/{owner}/{repo}"], - getAccessRestrictions: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions"], - getAdminBranchProtection: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"], - getAllEnvironments: ["GET /repos/{owner}/{repo}/environments"], - getAllStatusCheckContexts: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts"], - getAllTopics: ["GET /repos/{owner}/{repo}/topics"], - getAppsWithAccessToProtectedBranch: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps"], - getAutolink: ["GET /repos/{owner}/{repo}/autolinks/{autolink_id}"], - getBranch: ["GET /repos/{owner}/{repo}/branches/{branch}"], - getBranchProtection: ["GET /repos/{owner}/{repo}/branches/{branch}/protection"], - getClones: ["GET /repos/{owner}/{repo}/traffic/clones"], - getCodeFrequencyStats: ["GET /repos/{owner}/{repo}/stats/code_frequency"], - getCollaboratorPermissionLevel: ["GET /repos/{owner}/{repo}/collaborators/{username}/permission"], - getCombinedStatusForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/status"], - getCommit: ["GET /repos/{owner}/{repo}/commits/{ref}"], - getCommitActivityStats: ["GET /repos/{owner}/{repo}/stats/commit_activity"], - getCommitComment: ["GET /repos/{owner}/{repo}/comments/{comment_id}"], - getCommitSignatureProtection: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures"], - getCommunityProfileMetrics: ["GET /repos/{owner}/{repo}/community/profile"], - getContent: ["GET /repos/{owner}/{repo}/contents/{path}"], - getContributorsStats: ["GET /repos/{owner}/{repo}/stats/contributors"], - getDeployKey: ["GET /repos/{owner}/{repo}/keys/{key_id}"], - getDeployment: ["GET /repos/{owner}/{repo}/deployments/{deployment_id}"], - getDeploymentStatus: ["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id}"], - getEnvironment: ["GET /repos/{owner}/{repo}/environments/{environment_name}"], - getLatestPagesBuild: ["GET /repos/{owner}/{repo}/pages/builds/latest"], - getLatestRelease: ["GET /repos/{owner}/{repo}/releases/latest"], - getPages: ["GET /repos/{owner}/{repo}/pages"], - getPagesBuild: ["GET /repos/{owner}/{repo}/pages/builds/{build_id}"], - getPagesHealthCheck: ["GET /repos/{owner}/{repo}/pages/health"], - getParticipationStats: ["GET /repos/{owner}/{repo}/stats/participation"], - getPullRequestReviewProtection: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"], - getPunchCardStats: ["GET /repos/{owner}/{repo}/stats/punch_card"], - getReadme: ["GET /repos/{owner}/{repo}/readme"], - getReadmeInDirectory: ["GET /repos/{owner}/{repo}/readme/{dir}"], - getRelease: ["GET /repos/{owner}/{repo}/releases/{release_id}"], - getReleaseAsset: ["GET /repos/{owner}/{repo}/releases/assets/{asset_id}"], - getReleaseByTag: ["GET /repos/{owner}/{repo}/releases/tags/{tag}"], - getStatusChecksProtection: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"], - getTeamsWithAccessToProtectedBranch: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams"], - getTopPaths: ["GET /repos/{owner}/{repo}/traffic/popular/paths"], - getTopReferrers: ["GET /repos/{owner}/{repo}/traffic/popular/referrers"], - getUsersWithAccessToProtectedBranch: ["GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users"], - getViews: ["GET /repos/{owner}/{repo}/traffic/views"], - getWebhook: ["GET /repos/{owner}/{repo}/hooks/{hook_id}"], - getWebhookConfigForRepo: ["GET /repos/{owner}/{repo}/hooks/{hook_id}/config"], - getWebhookDelivery: ["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}"], - listAutolinks: ["GET /repos/{owner}/{repo}/autolinks"], - listBranches: ["GET /repos/{owner}/{repo}/branches"], - listBranchesForHeadCommit: ["GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head"], - listCollaborators: ["GET /repos/{owner}/{repo}/collaborators"], - listCommentsForCommit: ["GET /repos/{owner}/{repo}/commits/{commit_sha}/comments"], - listCommitCommentsForRepo: ["GET /repos/{owner}/{repo}/comments"], - listCommitStatusesForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/statuses"], - listCommits: ["GET /repos/{owner}/{repo}/commits"], - listContributors: ["GET /repos/{owner}/{repo}/contributors"], - listDeployKeys: ["GET /repos/{owner}/{repo}/keys"], - listDeploymentStatuses: ["GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses"], - listDeployments: ["GET /repos/{owner}/{repo}/deployments"], - listForAuthenticatedUser: ["GET /user/repos"], - listForOrg: ["GET /orgs/{org}/repos"], - listForUser: ["GET /users/{username}/repos"], - listForks: ["GET /repos/{owner}/{repo}/forks"], - listInvitations: ["GET /repos/{owner}/{repo}/invitations"], - listInvitationsForAuthenticatedUser: ["GET /user/repository_invitations"], - listLanguages: ["GET /repos/{owner}/{repo}/languages"], - listPagesBuilds: ["GET /repos/{owner}/{repo}/pages/builds"], - listPublic: ["GET /repositories"], - listPullRequestsAssociatedWithCommit: ["GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls"], - listReleaseAssets: ["GET /repos/{owner}/{repo}/releases/{release_id}/assets"], - listReleases: ["GET /repos/{owner}/{repo}/releases"], - listTagProtection: ["GET /repos/{owner}/{repo}/tags/protection"], - listTags: ["GET /repos/{owner}/{repo}/tags"], - listTeams: ["GET /repos/{owner}/{repo}/teams"], - listWebhookDeliveries: ["GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries"], - listWebhooks: ["GET /repos/{owner}/{repo}/hooks"], - merge: ["POST /repos/{owner}/{repo}/merges"], - mergeUpstream: ["POST /repos/{owner}/{repo}/merge-upstream"], - pingWebhook: ["POST /repos/{owner}/{repo}/hooks/{hook_id}/pings"], - redeliverWebhookDelivery: ["POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts"], - removeAppAccessRestrictions: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", {}, { - mapToData: "apps" - }], - removeCollaborator: ["DELETE /repos/{owner}/{repo}/collaborators/{username}"], - removeStatusCheckContexts: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", {}, { - mapToData: "contexts" - }], - removeStatusCheckProtection: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"], - removeTeamAccessRestrictions: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", {}, { - mapToData: "teams" - }], - removeUserAccessRestrictions: ["DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", {}, { - mapToData: "users" - }], - renameBranch: ["POST /repos/{owner}/{repo}/branches/{branch}/rename"], - replaceAllTopics: ["PUT /repos/{owner}/{repo}/topics"], - requestPagesBuild: ["POST /repos/{owner}/{repo}/pages/builds"], - setAdminBranchProtection: ["POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins"], - setAppAccessRestrictions: ["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", {}, { - mapToData: "apps" - }], - setStatusCheckContexts: ["PUT /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", {}, { - mapToData: "contexts" - }], - setTeamAccessRestrictions: ["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", {}, { - mapToData: "teams" - }], - setUserAccessRestrictions: ["PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", {}, { - mapToData: "users" - }], - testPushWebhook: ["POST /repos/{owner}/{repo}/hooks/{hook_id}/tests"], - transfer: ["POST /repos/{owner}/{repo}/transfer"], - update: ["PATCH /repos/{owner}/{repo}"], - updateBranchProtection: ["PUT /repos/{owner}/{repo}/branches/{branch}/protection"], - updateCommitComment: ["PATCH /repos/{owner}/{repo}/comments/{comment_id}"], - updateInformationAboutPagesSite: ["PUT /repos/{owner}/{repo}/pages"], - updateInvitation: ["PATCH /repos/{owner}/{repo}/invitations/{invitation_id}"], - updatePullRequestReviewProtection: ["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews"], - updateRelease: ["PATCH /repos/{owner}/{repo}/releases/{release_id}"], - updateReleaseAsset: ["PATCH /repos/{owner}/{repo}/releases/assets/{asset_id}"], - updateStatusCheckPotection: ["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks", {}, { - renamed: ["repos", "updateStatusCheckProtection"] - }], - updateStatusCheckProtection: ["PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks"], - updateWebhook: ["PATCH /repos/{owner}/{repo}/hooks/{hook_id}"], - updateWebhookConfigForRepo: ["PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config"], - uploadReleaseAsset: ["POST /repos/{owner}/{repo}/releases/{release_id}/assets{?name,label}", { - baseUrl: "https://uploads.github.com" - }] - }, - search: { - code: ["GET /search/code"], - commits: ["GET /search/commits"], - issuesAndPullRequests: ["GET /search/issues"], - labels: ["GET /search/labels"], - repos: ["GET /search/repositories"], - topics: ["GET /search/topics"], - users: ["GET /search/users"] - }, - secretScanning: { - getAlert: ["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"], - listAlertsForEnterprise: ["GET /enterprises/{enterprise}/secret-scanning/alerts"], - listAlertsForOrg: ["GET /orgs/{org}/secret-scanning/alerts"], - listAlertsForRepo: ["GET /repos/{owner}/{repo}/secret-scanning/alerts"], - listLocationsForAlert: ["GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations"], - updateAlert: ["PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}"] - }, - teams: { - addOrUpdateMembershipForUserInOrg: ["PUT /orgs/{org}/teams/{team_slug}/memberships/{username}"], - addOrUpdateProjectPermissionsInOrg: ["PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}"], - addOrUpdateRepoPermissionsInOrg: ["PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"], - checkPermissionsForProjectInOrg: ["GET /orgs/{org}/teams/{team_slug}/projects/{project_id}"], - checkPermissionsForRepoInOrg: ["GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"], - create: ["POST /orgs/{org}/teams"], - createDiscussionCommentInOrg: ["POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"], - createDiscussionInOrg: ["POST /orgs/{org}/teams/{team_slug}/discussions"], - deleteDiscussionCommentInOrg: ["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"], - deleteDiscussionInOrg: ["DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"], - deleteInOrg: ["DELETE /orgs/{org}/teams/{team_slug}"], - getByName: ["GET /orgs/{org}/teams/{team_slug}"], - getDiscussionCommentInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"], - getDiscussionInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"], - getMembershipForUserInOrg: ["GET /orgs/{org}/teams/{team_slug}/memberships/{username}"], - list: ["GET /orgs/{org}/teams"], - listChildInOrg: ["GET /orgs/{org}/teams/{team_slug}/teams"], - listDiscussionCommentsInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments"], - listDiscussionsInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions"], - listForAuthenticatedUser: ["GET /user/teams"], - listMembersInOrg: ["GET /orgs/{org}/teams/{team_slug}/members"], - listPendingInvitationsInOrg: ["GET /orgs/{org}/teams/{team_slug}/invitations"], - listProjectsInOrg: ["GET /orgs/{org}/teams/{team_slug}/projects"], - listReposInOrg: ["GET /orgs/{org}/teams/{team_slug}/repos"], - removeMembershipForUserInOrg: ["DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}"], - removeProjectInOrg: ["DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}"], - removeRepoInOrg: ["DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}"], - updateDiscussionCommentInOrg: ["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}"], - updateDiscussionInOrg: ["PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}"], - updateInOrg: ["PATCH /orgs/{org}/teams/{team_slug}"] - }, - users: { - addEmailForAuthenticated: ["POST /user/emails", {}, { - renamed: ["users", "addEmailForAuthenticatedUser"] - }], - addEmailForAuthenticatedUser: ["POST /user/emails"], - block: ["PUT /user/blocks/{username}"], - checkBlocked: ["GET /user/blocks/{username}"], - checkFollowingForUser: ["GET /users/{username}/following/{target_user}"], - checkPersonIsFollowedByAuthenticated: ["GET /user/following/{username}"], - createGpgKeyForAuthenticated: ["POST /user/gpg_keys", {}, { - renamed: ["users", "createGpgKeyForAuthenticatedUser"] - }], - createGpgKeyForAuthenticatedUser: ["POST /user/gpg_keys"], - createPublicSshKeyForAuthenticated: ["POST /user/keys", {}, { - renamed: ["users", "createPublicSshKeyForAuthenticatedUser"] - }], - createPublicSshKeyForAuthenticatedUser: ["POST /user/keys"], - deleteEmailForAuthenticated: ["DELETE /user/emails", {}, { - renamed: ["users", "deleteEmailForAuthenticatedUser"] - }], - deleteEmailForAuthenticatedUser: ["DELETE /user/emails"], - deleteGpgKeyForAuthenticated: ["DELETE /user/gpg_keys/{gpg_key_id}", {}, { - renamed: ["users", "deleteGpgKeyForAuthenticatedUser"] - }], - deleteGpgKeyForAuthenticatedUser: ["DELETE /user/gpg_keys/{gpg_key_id}"], - deletePublicSshKeyForAuthenticated: ["DELETE /user/keys/{key_id}", {}, { - renamed: ["users", "deletePublicSshKeyForAuthenticatedUser"] - }], - deletePublicSshKeyForAuthenticatedUser: ["DELETE /user/keys/{key_id}"], - follow: ["PUT /user/following/{username}"], - getAuthenticated: ["GET /user"], - getByUsername: ["GET /users/{username}"], - getContextForUser: ["GET /users/{username}/hovercard"], - getGpgKeyForAuthenticated: ["GET /user/gpg_keys/{gpg_key_id}", {}, { - renamed: ["users", "getGpgKeyForAuthenticatedUser"] - }], - getGpgKeyForAuthenticatedUser: ["GET /user/gpg_keys/{gpg_key_id}"], - getPublicSshKeyForAuthenticated: ["GET /user/keys/{key_id}", {}, { - renamed: ["users", "getPublicSshKeyForAuthenticatedUser"] - }], - getPublicSshKeyForAuthenticatedUser: ["GET /user/keys/{key_id}"], - list: ["GET /users"], - listBlockedByAuthenticated: ["GET /user/blocks", {}, { - renamed: ["users", "listBlockedByAuthenticatedUser"] - }], - listBlockedByAuthenticatedUser: ["GET /user/blocks"], - listEmailsForAuthenticated: ["GET /user/emails", {}, { - renamed: ["users", "listEmailsForAuthenticatedUser"] - }], - listEmailsForAuthenticatedUser: ["GET /user/emails"], - listFollowedByAuthenticated: ["GET /user/following", {}, { - renamed: ["users", "listFollowedByAuthenticatedUser"] - }], - listFollowedByAuthenticatedUser: ["GET /user/following"], - listFollowersForAuthenticatedUser: ["GET /user/followers"], - listFollowersForUser: ["GET /users/{username}/followers"], - listFollowingForUser: ["GET /users/{username}/following"], - listGpgKeysForAuthenticated: ["GET /user/gpg_keys", {}, { - renamed: ["users", "listGpgKeysForAuthenticatedUser"] - }], - listGpgKeysForAuthenticatedUser: ["GET /user/gpg_keys"], - listGpgKeysForUser: ["GET /users/{username}/gpg_keys"], - listPublicEmailsForAuthenticated: ["GET /user/public_emails", {}, { - renamed: ["users", "listPublicEmailsForAuthenticatedUser"] - }], - listPublicEmailsForAuthenticatedUser: ["GET /user/public_emails"], - listPublicKeysForUser: ["GET /users/{username}/keys"], - listPublicSshKeysForAuthenticated: ["GET /user/keys", {}, { - renamed: ["users", "listPublicSshKeysForAuthenticatedUser"] - }], - listPublicSshKeysForAuthenticatedUser: ["GET /user/keys"], - setPrimaryEmailVisibilityForAuthenticated: ["PATCH /user/email/visibility", {}, { - renamed: ["users", "setPrimaryEmailVisibilityForAuthenticatedUser"] - }], - setPrimaryEmailVisibilityForAuthenticatedUser: ["PATCH /user/email/visibility"], - unblock: ["DELETE /user/blocks/{username}"], - unfollow: ["DELETE /user/following/{username}"], - updateAuthenticated: ["PATCH /user"] - } -}; - -const VERSION = "5.16.2"; - -function endpointsToMethods(octokit, endpointsMap) { - const newMethods = {}; - - for (const [scope, endpoints] of Object.entries(endpointsMap)) { - for (const [methodName, endpoint] of Object.entries(endpoints)) { - const [route, defaults, decorations] = endpoint; - const [method, url] = route.split(/ /); - const endpointDefaults = Object.assign({ - method, - url - }, defaults); - - if (!newMethods[scope]) { - newMethods[scope] = {}; - } - - const scopeMethods = newMethods[scope]; - - if (decorations) { - scopeMethods[methodName] = decorate(octokit, scope, methodName, endpointDefaults, decorations); - continue; - } - - scopeMethods[methodName] = octokit.request.defaults(endpointDefaults); - } - } - - return newMethods; -} - -function decorate(octokit, scope, methodName, defaults, decorations) { - const requestWithDefaults = octokit.request.defaults(defaults); - /* istanbul ignore next */ - - function withDecorations(...args) { - // @ts-ignore https://github.com/microsoft/TypeScript/issues/25488 - let options = requestWithDefaults.endpoint.merge(...args); // There are currently no other decorations than `.mapToData` - - if (decorations.mapToData) { - options = Object.assign({}, options, { - data: options[decorations.mapToData], - [decorations.mapToData]: undefined - }); - return requestWithDefaults(options); - } - - if (decorations.renamed) { - const [newScope, newMethodName] = decorations.renamed; - octokit.log.warn(`octokit.${scope}.${methodName}() has been renamed to octokit.${newScope}.${newMethodName}()`); - } - - if (decorations.deprecated) { - octokit.log.warn(decorations.deprecated); - } - - if (decorations.renamedParameters) { - // @ts-ignore https://github.com/microsoft/TypeScript/issues/25488 - const options = requestWithDefaults.endpoint.merge(...args); - - for (const [name, alias] of Object.entries(decorations.renamedParameters)) { - if (name in options) { - octokit.log.warn(`"${name}" parameter is deprecated for "octokit.${scope}.${methodName}()". Use "${alias}" instead`); - - if (!(alias in options)) { - options[alias] = options[name]; - } - - delete options[name]; - } - } - - return requestWithDefaults(options); - } // @ts-ignore https://github.com/microsoft/TypeScript/issues/25488 - - - return requestWithDefaults(...args); - } - - return Object.assign(withDecorations, requestWithDefaults); -} - -function restEndpointMethods(octokit) { - const api = endpointsToMethods(octokit, Endpoints); - return { - rest: api - }; -} -restEndpointMethods.VERSION = VERSION; -function legacyRestEndpointMethods(octokit) { - const api = endpointsToMethods(octokit, Endpoints); - return _objectSpread2(_objectSpread2({}, api), {}, { - rest: api - }); -} -legacyRestEndpointMethods.VERSION = VERSION; - -exports.legacyRestEndpointMethods = legacyRestEndpointMethods; -exports.restEndpointMethods = restEndpointMethods; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 9968: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var BottleneckLight = _interopDefault(__nccwpck_require__(1174)); - -const VERSION = "4.1.0"; - -const noop = () => Promise.resolve(); // @ts-expect-error - - -function wrapRequest(state, request, options) { - return state.retryLimiter.schedule(doRequest, state, request, options); -} // @ts-expect-error - -async function doRequest(state, request, options) { - const isWrite = options.method !== "GET" && options.method !== "HEAD"; - const { - pathname - } = new URL(options.url, "http://github.test"); - const isSearch = options.method === "GET" && pathname.startsWith("/search/"); - const isGraphQL = pathname.startsWith("/graphql"); - const retryCount = ~~options.request.retryCount; - const jobOptions = retryCount > 0 ? { - priority: 0, - weight: 0 - } : {}; - - if (state.clustering) { - // Remove a job from Redis if it has not completed or failed within 60s - // Examples: Node process terminated, client disconnected, etc. - // @ts-expect-error - jobOptions.expiration = 1000 * 60; - } // Guarantee at least 1000ms between writes - // GraphQL can also trigger writes - - - if (isWrite || isGraphQL) { - await state.write.key(state.id).schedule(jobOptions, noop); - } // Guarantee at least 3000ms between requests that trigger notifications - - - if (isWrite && state.triggersNotification(pathname)) { - await state.notifications.key(state.id).schedule(jobOptions, noop); - } // Guarantee at least 2000ms between search requests - - - if (isSearch) { - await state.search.key(state.id).schedule(jobOptions, noop); - } - - const req = state.global.key(state.id).schedule(jobOptions, request, options); - - if (isGraphQL) { - const res = await req; - - if (res.data.errors != null && // @ts-expect-error - res.data.errors.some(error => error.type === "RATE_LIMITED")) { - const error = Object.assign(new Error("GraphQL Rate Limit Exceeded"), { - response: res, - data: res.data - }); - throw error; - } - } - - return req; -} - -var triggersNotificationPaths = ["/orgs/{org}/invitations", "/orgs/{org}/invitations/{invitation_id}", "/orgs/{org}/teams/{team_slug}/discussions", "/orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments", "/repos/{owner}/{repo}/collaborators/{username}", "/repos/{owner}/{repo}/commits/{commit_sha}/comments", "/repos/{owner}/{repo}/issues", "/repos/{owner}/{repo}/issues/{issue_number}/comments", "/repos/{owner}/{repo}/pulls", "/repos/{owner}/{repo}/pulls/{pull_number}/comments", "/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies", "/repos/{owner}/{repo}/pulls/{pull_number}/merge", "/repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers", "/repos/{owner}/{repo}/pulls/{pull_number}/reviews", "/repos/{owner}/{repo}/releases", "/teams/{team_id}/discussions", "/teams/{team_id}/discussions/{discussion_number}/comments"]; - -function routeMatcher(paths) { - // EXAMPLE. For the following paths: - - /* [ - "/orgs/{org}/invitations", - "/repos/{owner}/{repo}/collaborators/{username}" - ] */ - const regexes = paths.map(path => path.split("/").map(c => c.startsWith("{") ? "(?:.+?)" : c).join("/")); // 'regexes' would contain: - - /* [ - '/orgs/(?:.+?)/invitations', - '/repos/(?:.+?)/(?:.+?)/collaborators/(?:.+?)' - ] */ - - const regex = `^(?:${regexes.map(r => `(?:${r})`).join("|")})[^/]*$`; // 'regex' would contain: - - /* - ^(?:(?:\/orgs\/(?:.+?)\/invitations)|(?:\/repos\/(?:.+?)\/(?:.+?)\/collaborators\/(?:.+?)))[^\/]*$ - It may look scary, but paste it into https://www.debuggex.com/ - and it will make a lot more sense! - */ - - return new RegExp(regex, "i"); -} - -// @ts-expect-error - -const regex = routeMatcher(triggersNotificationPaths); -const triggersNotification = regex.test.bind(regex); -const groups = {}; // @ts-expect-error - -const createGroups = function (Bottleneck, common) { - groups.global = new Bottleneck.Group({ - id: "octokit-global", - maxConcurrent: 10, - ...common - }); - groups.search = new Bottleneck.Group({ - id: "octokit-search", - maxConcurrent: 1, - minTime: 2000, - ...common - }); - groups.write = new Bottleneck.Group({ - id: "octokit-write", - maxConcurrent: 1, - minTime: 1000, - ...common - }); - groups.notifications = new Bottleneck.Group({ - id: "octokit-notifications", - maxConcurrent: 1, - minTime: 3000, - ...common - }); -}; - -function throttling(octokit, octokitOptions) { - const { - enabled = true, - Bottleneck = BottleneckLight, - id = "no-id", - timeout = 1000 * 60 * 2, - // Redis TTL: 2 minutes - connection - } = octokitOptions.throttle || {}; - - if (!enabled) { - return {}; - } - - const common = { - connection, - timeout - }; - - if (groups.global == null) { - createGroups(Bottleneck, common); - } - - const state = Object.assign({ - clustering: connection != null, - triggersNotification, - minimumSecondaryRateRetryAfter: 5, - retryAfterBaseValue: 1000, - retryLimiter: new Bottleneck(), - id, - ...groups - }, octokitOptions.throttle); - const isUsingDeprecatedOnAbuseLimitHandler = typeof state.onAbuseLimit === "function" && state.onAbuseLimit; - - if (typeof (isUsingDeprecatedOnAbuseLimitHandler ? state.onAbuseLimit : state.onSecondaryRateLimit) !== "function" || typeof state.onRateLimit !== "function") { - throw new Error(`octokit/plugin-throttling error: - You must pass the onSecondaryRateLimit and onRateLimit error handlers. - See https://github.com/octokit/rest.js#throttling - - const octokit = new Octokit({ - throttle: { - onSecondaryRateLimit: (retryAfter, options) => {/* ... */}, - onRateLimit: (retryAfter, options) => {/* ... */} - } - }) - `); - } - - const events = {}; - const emitter = new Bottleneck.Events(events); // @ts-expect-error - - events.on("secondary-limit", isUsingDeprecatedOnAbuseLimitHandler ? function (...args) { - octokit.log.warn("[@octokit/plugin-throttling] `onAbuseLimit()` is deprecated and will be removed in a future release of `@octokit/plugin-throttling`, please use the `onSecondaryRateLimit` handler instead"); - return state.onAbuseLimit(...args); - } : state.onSecondaryRateLimit); // @ts-expect-error - - events.on("rate-limit", state.onRateLimit); // @ts-expect-error - - events.on("error", e => octokit.log.warn("Error in throttling-plugin limit handler", e)); // @ts-expect-error - - state.retryLimiter.on("failed", async function (error, info) { - const options = info.args[info.args.length - 1]; - const { - pathname - } = new URL(options.url, "http://github.test"); - const shouldRetryGraphQL = pathname.startsWith("/graphql") && error.status !== 401; - - if (!(shouldRetryGraphQL || error.status === 403)) { - return; - } - - const retryCount = ~~options.request.retryCount; - options.request.retryCount = retryCount; - const { - wantRetry, - retryAfter = 0 - } = await async function () { - if (/\bsecondary rate\b/i.test(error.message)) { - // The user has hit the secondary rate limit. (REST and GraphQL) - // https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits - // The Retry-After header can sometimes be blank when hitting a secondary rate limit, - // but is always present after 2-3s, so make sure to set `retryAfter` to at least 5s by default. - const retryAfter = Math.max(~~error.response.headers["retry-after"], state.minimumSecondaryRateRetryAfter); - const wantRetry = await emitter.trigger("secondary-limit", retryAfter, options, octokit); - return { - wantRetry, - retryAfter - }; - } - - if (error.response.headers != null && error.response.headers["x-ratelimit-remaining"] === "0") { - // The user has used all their allowed calls for the current time period (REST and GraphQL) - // https://docs.github.com/en/rest/reference/rate-limit (REST) - // https://docs.github.com/en/graphql/overview/resource-limitations#rate-limit (GraphQL) - const rateLimitReset = new Date(~~error.response.headers["x-ratelimit-reset"] * 1000).getTime(); - const retryAfter = Math.max(Math.ceil((rateLimitReset - Date.now()) / 1000), 0); - const wantRetry = await emitter.trigger("rate-limit", retryAfter, options, octokit); - return { - wantRetry, - retryAfter - }; - } - - return {}; - }(); - - if (wantRetry) { - options.request.retryCount++; - return retryAfter * state.retryAfterBaseValue; - } - }); - octokit.hook.wrap("request", wrapRequest.bind(null, state)); - return {}; -} -throttling.VERSION = VERSION; -throttling.triggersNotification = triggersNotification; - -exports.throttling = throttling; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 537: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var deprecation = __nccwpck_require__(8932); -var once = _interopDefault(__nccwpck_require__(1223)); - -const logOnceCode = once(deprecation => console.warn(deprecation)); -const logOnceHeaders = once(deprecation => console.warn(deprecation)); -/** - * Error with extra properties to help with debugging - */ -class RequestError extends Error { - constructor(message, statusCode, options) { - super(message); - // Maintains proper stack trace (only available on V8) - /* istanbul ignore next */ - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - this.name = "HttpError"; - this.status = statusCode; - let headers; - if ("headers" in options && typeof options.headers !== "undefined") { - headers = options.headers; - } - if ("response" in options) { - this.response = options.response; - headers = options.response.headers; - } - // redact request credentials without mutating original request options - const requestCopy = Object.assign({}, options.request); - if (options.request.headers.authorization) { - requestCopy.headers = Object.assign({}, options.request.headers, { - authorization: options.request.headers.authorization.replace(/ .*$/, " [REDACTED]") - }); - } - requestCopy.url = requestCopy.url - // client_id & client_secret can be passed as URL query parameters to increase rate limit - // see https://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications - .replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]") - // OAuth tokens can be passed as URL query parameters, although it is not recommended - // see https://developer.github.com/v3/#oauth2-token-sent-in-a-header - .replace(/\baccess_token=\w+/g, "access_token=[REDACTED]"); - this.request = requestCopy; - // deprecations - Object.defineProperty(this, "code", { - get() { - logOnceCode(new deprecation.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`.")); - return statusCode; - } - }); - Object.defineProperty(this, "headers", { - get() { - logOnceHeaders(new deprecation.Deprecation("[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`.")); - return headers || {}; - } - }); - } -} - -exports.RequestError = RequestError; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 3682: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -var register = __nccwpck_require__(4670) -var addHook = __nccwpck_require__(5549) -var removeHook = __nccwpck_require__(6819) - -// bind with array of arguments: https://stackoverflow.com/a/21792913 -var bind = Function.bind -var bindable = bind.bind(bind) - -function bindApi (hook, state, name) { - var removeHookRef = bindable(removeHook, null).apply(null, name ? [state, name] : [state]) - hook.api = { remove: removeHookRef } - hook.remove = removeHookRef - - ;['before', 'error', 'after', 'wrap'].forEach(function (kind) { - var args = name ? [state, kind, name] : [state, kind] - hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args) - }) -} - -function HookSingular () { - var singularHookName = 'h' - var singularHookState = { - registry: {} - } - var singularHook = register.bind(null, singularHookState, singularHookName) - bindApi(singularHook, singularHookState, singularHookName) - return singularHook -} - -function HookCollection () { - var state = { - registry: {} - } - - var hook = register.bind(null, state) - bindApi(hook, state) - - return hook -} - -var collectionHookDeprecationMessageDisplayed = false -function Hook () { - if (!collectionHookDeprecationMessageDisplayed) { - console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4') - collectionHookDeprecationMessageDisplayed = true - } - return HookCollection() -} - -Hook.Singular = HookSingular.bind() -Hook.Collection = HookCollection.bind() - -module.exports = Hook -// expose constructors as a named property for TypeScript -module.exports.Hook = Hook -module.exports.Singular = Hook.Singular -module.exports.Collection = Hook.Collection - - -/***/ }), - -/***/ 5549: -/***/ ((module) => { - -module.exports = addHook; - -function addHook(state, kind, name, hook) { - var orig = hook; - if (!state.registry[name]) { - state.registry[name] = []; - } - - if (kind === "before") { - hook = function (method, options) { - return Promise.resolve() - .then(orig.bind(null, options)) - .then(method.bind(null, options)); - }; - } - - if (kind === "after") { - hook = function (method, options) { - var result; - return Promise.resolve() - .then(method.bind(null, options)) - .then(function (result_) { - result = result_; - return orig(result, options); - }) - .then(function () { - return result; - }); - }; - } - - if (kind === "error") { - hook = function (method, options) { - return Promise.resolve() - .then(method.bind(null, options)) - .catch(function (error) { - return orig(error, options); - }); - }; - } - - state.registry[name].push({ - hook: hook, - orig: orig, - }); -} - - -/***/ }), - -/***/ 4670: -/***/ ((module) => { - -module.exports = register; - -function register(state, name, method, options) { - if (typeof method !== "function") { - throw new Error("method for before hook must be a function"); - } - - if (!options) { - options = {}; - } - - if (Array.isArray(name)) { - return name.reverse().reduce(function (callback, name) { - return register.bind(null, state, name, callback, options); - }, method)(); - } - - return Promise.resolve().then(function () { - if (!state.registry[name]) { - return method(options); - } - - return state.registry[name].reduce(function (method, registered) { - return registered.hook.bind(null, method, options); - }, method)(); - }); -} - - -/***/ }), - -/***/ 6819: -/***/ ((module) => { - -module.exports = removeHook; - -function removeHook(state, name, method) { - if (!state.registry[name]) { - return; - } - - var index = state.registry[name] - .map(function (registered) { - return registered.orig; - }) - .indexOf(method); - - if (index === -1) { - return; - } - - state.registry[name].splice(index, 1); -} - - -/***/ }), - -/***/ 1174: -/***/ (function(module) { - -/** - * This file contains the Bottleneck library (MIT), compiled to ES2017, and without Clustering support. - * https://github.com/SGrondin/bottleneck - */ -(function (global, factory) { - true ? module.exports = factory() : - 0; -}(this, (function () { 'use strict'; - - var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; - - function getCjsExportFromNamespace (n) { - return n && n['default'] || n; - } - - var load = function(received, defaults, onto = {}) { - var k, ref, v; - for (k in defaults) { - v = defaults[k]; - onto[k] = (ref = received[k]) != null ? ref : v; - } - return onto; - }; - - var overwrite = function(received, defaults, onto = {}) { - var k, v; - for (k in received) { - v = received[k]; - if (defaults[k] !== void 0) { - onto[k] = v; - } - } - return onto; - }; - - var parser = { - load: load, - overwrite: overwrite - }; - - var DLList; - - DLList = class DLList { - constructor(incr, decr) { - this.incr = incr; - this.decr = decr; - this._first = null; - this._last = null; - this.length = 0; - } - - push(value) { - var node; - this.length++; - if (typeof this.incr === "function") { - this.incr(); - } - node = { - value, - prev: this._last, - next: null - }; - if (this._last != null) { - this._last.next = node; - this._last = node; - } else { - this._first = this._last = node; - } - return void 0; - } - - shift() { - var value; - if (this._first == null) { - return; - } else { - this.length--; - if (typeof this.decr === "function") { - this.decr(); - } - } - value = this._first.value; - if ((this._first = this._first.next) != null) { - this._first.prev = null; - } else { - this._last = null; - } - return value; - } - - first() { - if (this._first != null) { - return this._first.value; - } - } - - getArray() { - var node, ref, results; - node = this._first; - results = []; - while (node != null) { - results.push((ref = node, node = node.next, ref.value)); - } - return results; - } - - forEachShift(cb) { - var node; - node = this.shift(); - while (node != null) { - (cb(node), node = this.shift()); - } - return void 0; - } - - debug() { - var node, ref, ref1, ref2, results; - node = this._first; - results = []; - while (node != null) { - results.push((ref = node, node = node.next, { - value: ref.value, - prev: (ref1 = ref.prev) != null ? ref1.value : void 0, - next: (ref2 = ref.next) != null ? ref2.value : void 0 - })); - } - return results; - } - - }; - - var DLList_1 = DLList; - - var Events; - - Events = class Events { - constructor(instance) { - this.instance = instance; - this._events = {}; - if ((this.instance.on != null) || (this.instance.once != null) || (this.instance.removeAllListeners != null)) { - throw new Error("An Emitter already exists for this object"); - } - this.instance.on = (name, cb) => { - return this._addListener(name, "many", cb); - }; - this.instance.once = (name, cb) => { - return this._addListener(name, "once", cb); - }; - this.instance.removeAllListeners = (name = null) => { - if (name != null) { - return delete this._events[name]; - } else { - return this._events = {}; - } - }; - } - - _addListener(name, status, cb) { - var base; - if ((base = this._events)[name] == null) { - base[name] = []; - } - this._events[name].push({cb, status}); - return this.instance; - } - - listenerCount(name) { - if (this._events[name] != null) { - return this._events[name].length; - } else { - return 0; - } - } - - async trigger(name, ...args) { - var e, promises; - try { - if (name !== "debug") { - this.trigger("debug", `Event triggered: ${name}`, args); - } - if (this._events[name] == null) { - return; - } - this._events[name] = this._events[name].filter(function(listener) { - return listener.status !== "none"; - }); - promises = this._events[name].map(async(listener) => { - var e, returned; - if (listener.status === "none") { - return; - } - if (listener.status === "once") { - listener.status = "none"; - } - try { - returned = typeof listener.cb === "function" ? listener.cb(...args) : void 0; - if (typeof (returned != null ? returned.then : void 0) === "function") { - return (await returned); - } else { - return returned; - } - } catch (error) { - e = error; - { - this.trigger("error", e); - } - return null; - } - }); - return ((await Promise.all(promises))).find(function(x) { - return x != null; - }); - } catch (error) { - e = error; - { - this.trigger("error", e); - } - return null; - } - } - - }; - - var Events_1 = Events; - - var DLList$1, Events$1, Queues; - - DLList$1 = DLList_1; - - Events$1 = Events_1; - - Queues = class Queues { - constructor(num_priorities) { - var i; - this.Events = new Events$1(this); - this._length = 0; - this._lists = (function() { - var j, ref, results; - results = []; - for (i = j = 1, ref = num_priorities; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) { - results.push(new DLList$1((() => { - return this.incr(); - }), (() => { - return this.decr(); - }))); - } - return results; - }).call(this); - } - - incr() { - if (this._length++ === 0) { - return this.Events.trigger("leftzero"); - } - } - - decr() { - if (--this._length === 0) { - return this.Events.trigger("zero"); - } - } - - push(job) { - return this._lists[job.options.priority].push(job); - } - - queued(priority) { - if (priority != null) { - return this._lists[priority].length; - } else { - return this._length; - } - } - - shiftAll(fn) { - return this._lists.forEach(function(list) { - return list.forEachShift(fn); - }); - } - - getFirst(arr = this._lists) { - var j, len, list; - for (j = 0, len = arr.length; j < len; j++) { - list = arr[j]; - if (list.length > 0) { - return list; - } - } - return []; - } - - shiftLastFrom(priority) { - return this.getFirst(this._lists.slice(priority).reverse()).shift(); - } - - }; - - var Queues_1 = Queues; - - var BottleneckError; - - BottleneckError = class BottleneckError extends Error {}; - - var BottleneckError_1 = BottleneckError; - - var BottleneckError$1, DEFAULT_PRIORITY, Job, NUM_PRIORITIES, parser$1; - - NUM_PRIORITIES = 10; - - DEFAULT_PRIORITY = 5; - - parser$1 = parser; - - BottleneckError$1 = BottleneckError_1; - - Job = class Job { - constructor(task, args, options, jobDefaults, rejectOnDrop, Events, _states, Promise) { - this.task = task; - this.args = args; - this.rejectOnDrop = rejectOnDrop; - this.Events = Events; - this._states = _states; - this.Promise = Promise; - this.options = parser$1.load(options, jobDefaults); - this.options.priority = this._sanitizePriority(this.options.priority); - if (this.options.id === jobDefaults.id) { - this.options.id = `${this.options.id}-${this._randomIndex()}`; - } - this.promise = new this.Promise((_resolve, _reject) => { - this._resolve = _resolve; - this._reject = _reject; - }); - this.retryCount = 0; - } - - _sanitizePriority(priority) { - var sProperty; - sProperty = ~~priority !== priority ? DEFAULT_PRIORITY : priority; - if (sProperty < 0) { - return 0; - } else if (sProperty > NUM_PRIORITIES - 1) { - return NUM_PRIORITIES - 1; - } else { - return sProperty; - } - } - - _randomIndex() { - return Math.random().toString(36).slice(2); - } - - doDrop({error, message = "This job has been dropped by Bottleneck"} = {}) { - if (this._states.remove(this.options.id)) { - if (this.rejectOnDrop) { - this._reject(error != null ? error : new BottleneckError$1(message)); - } - this.Events.trigger("dropped", {args: this.args, options: this.options, task: this.task, promise: this.promise}); - return true; - } else { - return false; - } - } - - _assertStatus(expected) { - var status; - status = this._states.jobStatus(this.options.id); - if (!(status === expected || (expected === "DONE" && status === null))) { - throw new BottleneckError$1(`Invalid job status ${status}, expected ${expected}. Please open an issue at https://github.com/SGrondin/bottleneck/issues`); - } - } - - doReceive() { - this._states.start(this.options.id); - return this.Events.trigger("received", {args: this.args, options: this.options}); - } - - doQueue(reachedHWM, blocked) { - this._assertStatus("RECEIVED"); - this._states.next(this.options.id); - return this.Events.trigger("queued", {args: this.args, options: this.options, reachedHWM, blocked}); - } - - doRun() { - if (this.retryCount === 0) { - this._assertStatus("QUEUED"); - this._states.next(this.options.id); - } else { - this._assertStatus("EXECUTING"); - } - return this.Events.trigger("scheduled", {args: this.args, options: this.options}); - } - - async doExecute(chained, clearGlobalState, run, free) { - var error, eventInfo, passed; - if (this.retryCount === 0) { - this._assertStatus("RUNNING"); - this._states.next(this.options.id); - } else { - this._assertStatus("EXECUTING"); - } - eventInfo = {args: this.args, options: this.options, retryCount: this.retryCount}; - this.Events.trigger("executing", eventInfo); - try { - passed = (await (chained != null ? chained.schedule(this.options, this.task, ...this.args) : this.task(...this.args))); - if (clearGlobalState()) { - this.doDone(eventInfo); - await free(this.options, eventInfo); - this._assertStatus("DONE"); - return this._resolve(passed); - } - } catch (error1) { - error = error1; - return this._onFailure(error, eventInfo, clearGlobalState, run, free); - } - } - - doExpire(clearGlobalState, run, free) { - var error, eventInfo; - if (this._states.jobStatus(this.options.id === "RUNNING")) { - this._states.next(this.options.id); - } - this._assertStatus("EXECUTING"); - eventInfo = {args: this.args, options: this.options, retryCount: this.retryCount}; - error = new BottleneckError$1(`This job timed out after ${this.options.expiration} ms.`); - return this._onFailure(error, eventInfo, clearGlobalState, run, free); - } - - async _onFailure(error, eventInfo, clearGlobalState, run, free) { - var retry, retryAfter; - if (clearGlobalState()) { - retry = (await this.Events.trigger("failed", error, eventInfo)); - if (retry != null) { - retryAfter = ~~retry; - this.Events.trigger("retry", `Retrying ${this.options.id} after ${retryAfter} ms`, eventInfo); - this.retryCount++; - return run(retryAfter); - } else { - this.doDone(eventInfo); - await free(this.options, eventInfo); - this._assertStatus("DONE"); - return this._reject(error); - } - } - } - - doDone(eventInfo) { - this._assertStatus("EXECUTING"); - this._states.next(this.options.id); - return this.Events.trigger("done", eventInfo); - } - - }; - - var Job_1 = Job; - - var BottleneckError$2, LocalDatastore, parser$2; - - parser$2 = parser; - - BottleneckError$2 = BottleneckError_1; - - LocalDatastore = class LocalDatastore { - constructor(instance, storeOptions, storeInstanceOptions) { - this.instance = instance; - this.storeOptions = storeOptions; - this.clientId = this.instance._randomIndex(); - parser$2.load(storeInstanceOptions, storeInstanceOptions, this); - this._nextRequest = this._lastReservoirRefresh = this._lastReservoirIncrease = Date.now(); - this._running = 0; - this._done = 0; - this._unblockTime = 0; - this.ready = this.Promise.resolve(); - this.clients = {}; - this._startHeartbeat(); - } - - _startHeartbeat() { - var base; - if ((this.heartbeat == null) && (((this.storeOptions.reservoirRefreshInterval != null) && (this.storeOptions.reservoirRefreshAmount != null)) || ((this.storeOptions.reservoirIncreaseInterval != null) && (this.storeOptions.reservoirIncreaseAmount != null)))) { - return typeof (base = (this.heartbeat = setInterval(() => { - var amount, incr, maximum, now, reservoir; - now = Date.now(); - if ((this.storeOptions.reservoirRefreshInterval != null) && now >= this._lastReservoirRefresh + this.storeOptions.reservoirRefreshInterval) { - this._lastReservoirRefresh = now; - this.storeOptions.reservoir = this.storeOptions.reservoirRefreshAmount; - this.instance._drainAll(this.computeCapacity()); - } - if ((this.storeOptions.reservoirIncreaseInterval != null) && now >= this._lastReservoirIncrease + this.storeOptions.reservoirIncreaseInterval) { - ({ - reservoirIncreaseAmount: amount, - reservoirIncreaseMaximum: maximum, - reservoir - } = this.storeOptions); - this._lastReservoirIncrease = now; - incr = maximum != null ? Math.min(amount, maximum - reservoir) : amount; - if (incr > 0) { - this.storeOptions.reservoir += incr; - return this.instance._drainAll(this.computeCapacity()); - } - } - }, this.heartbeatInterval))).unref === "function" ? base.unref() : void 0; - } else { - return clearInterval(this.heartbeat); - } - } - - async __publish__(message) { - await this.yieldLoop(); - return this.instance.Events.trigger("message", message.toString()); - } - - async __disconnect__(flush) { - await this.yieldLoop(); - clearInterval(this.heartbeat); - return this.Promise.resolve(); - } - - yieldLoop(t = 0) { - return new this.Promise(function(resolve, reject) { - return setTimeout(resolve, t); - }); - } - - computePenalty() { - var ref; - return (ref = this.storeOptions.penalty) != null ? ref : (15 * this.storeOptions.minTime) || 5000; - } - - async __updateSettings__(options) { - await this.yieldLoop(); - parser$2.overwrite(options, options, this.storeOptions); - this._startHeartbeat(); - this.instance._drainAll(this.computeCapacity()); - return true; - } - - async __running__() { - await this.yieldLoop(); - return this._running; - } - - async __queued__() { - await this.yieldLoop(); - return this.instance.queued(); - } - - async __done__() { - await this.yieldLoop(); - return this._done; - } - - async __groupCheck__(time) { - await this.yieldLoop(); - return (this._nextRequest + this.timeout) < time; - } - - computeCapacity() { - var maxConcurrent, reservoir; - ({maxConcurrent, reservoir} = this.storeOptions); - if ((maxConcurrent != null) && (reservoir != null)) { - return Math.min(maxConcurrent - this._running, reservoir); - } else if (maxConcurrent != null) { - return maxConcurrent - this._running; - } else if (reservoir != null) { - return reservoir; - } else { - return null; - } - } - - conditionsCheck(weight) { - var capacity; - capacity = this.computeCapacity(); - return (capacity == null) || weight <= capacity; - } - - async __incrementReservoir__(incr) { - var reservoir; - await this.yieldLoop(); - reservoir = this.storeOptions.reservoir += incr; - this.instance._drainAll(this.computeCapacity()); - return reservoir; - } - - async __currentReservoir__() { - await this.yieldLoop(); - return this.storeOptions.reservoir; - } - - isBlocked(now) { - return this._unblockTime >= now; - } - - check(weight, now) { - return this.conditionsCheck(weight) && (this._nextRequest - now) <= 0; - } - - async __check__(weight) { - var now; - await this.yieldLoop(); - now = Date.now(); - return this.check(weight, now); - } - - async __register__(index, weight, expiration) { - var now, wait; - await this.yieldLoop(); - now = Date.now(); - if (this.conditionsCheck(weight)) { - this._running += weight; - if (this.storeOptions.reservoir != null) { - this.storeOptions.reservoir -= weight; - } - wait = Math.max(this._nextRequest - now, 0); - this._nextRequest = now + wait + this.storeOptions.minTime; - return { - success: true, - wait, - reservoir: this.storeOptions.reservoir - }; - } else { - return { - success: false - }; - } - } - - strategyIsBlock() { - return this.storeOptions.strategy === 3; - } - - async __submit__(queueLength, weight) { - var blocked, now, reachedHWM; - await this.yieldLoop(); - if ((this.storeOptions.maxConcurrent != null) && weight > this.storeOptions.maxConcurrent) { - throw new BottleneckError$2(`Impossible to add a job having a weight of ${weight} to a limiter having a maxConcurrent setting of ${this.storeOptions.maxConcurrent}`); - } - now = Date.now(); - reachedHWM = (this.storeOptions.highWater != null) && queueLength === this.storeOptions.highWater && !this.check(weight, now); - blocked = this.strategyIsBlock() && (reachedHWM || this.isBlocked(now)); - if (blocked) { - this._unblockTime = now + this.computePenalty(); - this._nextRequest = this._unblockTime + this.storeOptions.minTime; - this.instance._dropAllQueued(); - } - return { - reachedHWM, - blocked, - strategy: this.storeOptions.strategy - }; - } - - async __free__(index, weight) { - await this.yieldLoop(); - this._running -= weight; - this._done += weight; - this.instance._drainAll(this.computeCapacity()); - return { - running: this._running - }; - } - - }; - - var LocalDatastore_1 = LocalDatastore; - - var BottleneckError$3, States; - - BottleneckError$3 = BottleneckError_1; - - States = class States { - constructor(status1) { - this.status = status1; - this._jobs = {}; - this.counts = this.status.map(function() { - return 0; - }); - } - - next(id) { - var current, next; - current = this._jobs[id]; - next = current + 1; - if ((current != null) && next < this.status.length) { - this.counts[current]--; - this.counts[next]++; - return this._jobs[id]++; - } else if (current != null) { - this.counts[current]--; - return delete this._jobs[id]; - } - } - - start(id) { - var initial; - initial = 0; - this._jobs[id] = initial; - return this.counts[initial]++; - } - - remove(id) { - var current; - current = this._jobs[id]; - if (current != null) { - this.counts[current]--; - delete this._jobs[id]; - } - return current != null; - } - - jobStatus(id) { - var ref; - return (ref = this.status[this._jobs[id]]) != null ? ref : null; - } - - statusJobs(status) { - var k, pos, ref, results, v; - if (status != null) { - pos = this.status.indexOf(status); - if (pos < 0) { - throw new BottleneckError$3(`status must be one of ${this.status.join(', ')}`); - } - ref = this._jobs; - results = []; - for (k in ref) { - v = ref[k]; - if (v === pos) { - results.push(k); - } - } - return results; - } else { - return Object.keys(this._jobs); - } - } - - statusCounts() { - return this.counts.reduce(((acc, v, i) => { - acc[this.status[i]] = v; - return acc; - }), {}); - } - - }; - - var States_1 = States; - - var DLList$2, Sync; - - DLList$2 = DLList_1; - - Sync = class Sync { - constructor(name, Promise) { - this.schedule = this.schedule.bind(this); - this.name = name; - this.Promise = Promise; - this._running = 0; - this._queue = new DLList$2(); - } - - isEmpty() { - return this._queue.length === 0; - } - - async _tryToRun() { - var args, cb, error, reject, resolve, returned, task; - if ((this._running < 1) && this._queue.length > 0) { - this._running++; - ({task, args, resolve, reject} = this._queue.shift()); - cb = (await (async function() { - try { - returned = (await task(...args)); - return function() { - return resolve(returned); - }; - } catch (error1) { - error = error1; - return function() { - return reject(error); - }; - } - })()); - this._running--; - this._tryToRun(); - return cb(); - } - } - - schedule(task, ...args) { - var promise, reject, resolve; - resolve = reject = null; - promise = new this.Promise(function(_resolve, _reject) { - resolve = _resolve; - return reject = _reject; - }); - this._queue.push({task, args, resolve, reject}); - this._tryToRun(); - return promise; - } - - }; - - var Sync_1 = Sync; - - var version = "2.19.5"; - var version$1 = { - version: version - }; - - var version$2 = /*#__PURE__*/Object.freeze({ - version: version, - default: version$1 - }); - - var require$$2 = () => console.log('You must import the full version of Bottleneck in order to use this feature.'); - - var require$$3 = () => console.log('You must import the full version of Bottleneck in order to use this feature.'); - - var require$$4 = () => console.log('You must import the full version of Bottleneck in order to use this feature.'); - - var Events$2, Group, IORedisConnection$1, RedisConnection$1, Scripts$1, parser$3; - - parser$3 = parser; - - Events$2 = Events_1; - - RedisConnection$1 = require$$2; - - IORedisConnection$1 = require$$3; - - Scripts$1 = require$$4; - - Group = (function() { - class Group { - constructor(limiterOptions = {}) { - this.deleteKey = this.deleteKey.bind(this); - this.limiterOptions = limiterOptions; - parser$3.load(this.limiterOptions, this.defaults, this); - this.Events = new Events$2(this); - this.instances = {}; - this.Bottleneck = Bottleneck_1; - this._startAutoCleanup(); - this.sharedConnection = this.connection != null; - if (this.connection == null) { - if (this.limiterOptions.datastore === "redis") { - this.connection = new RedisConnection$1(Object.assign({}, this.limiterOptions, {Events: this.Events})); - } else if (this.limiterOptions.datastore === "ioredis") { - this.connection = new IORedisConnection$1(Object.assign({}, this.limiterOptions, {Events: this.Events})); - } - } - } - - key(key = "") { - var ref; - return (ref = this.instances[key]) != null ? ref : (() => { - var limiter; - limiter = this.instances[key] = new this.Bottleneck(Object.assign(this.limiterOptions, { - id: `${this.id}-${key}`, - timeout: this.timeout, - connection: this.connection - })); - this.Events.trigger("created", limiter, key); - return limiter; - })(); - } - - async deleteKey(key = "") { - var deleted, instance; - instance = this.instances[key]; - if (this.connection) { - deleted = (await this.connection.__runCommand__(['del', ...Scripts$1.allKeys(`${this.id}-${key}`)])); - } - if (instance != null) { - delete this.instances[key]; - await instance.disconnect(); - } - return (instance != null) || deleted > 0; - } - - limiters() { - var k, ref, results, v; - ref = this.instances; - results = []; - for (k in ref) { - v = ref[k]; - results.push({ - key: k, - limiter: v - }); - } - return results; - } - - keys() { - return Object.keys(this.instances); - } - - async clusterKeys() { - var cursor, end, found, i, k, keys, len, next, start; - if (this.connection == null) { - return this.Promise.resolve(this.keys()); - } - keys = []; - cursor = null; - start = `b_${this.id}-`.length; - end = "_settings".length; - while (cursor !== 0) { - [next, found] = (await this.connection.__runCommand__(["scan", cursor != null ? cursor : 0, "match", `b_${this.id}-*_settings`, "count", 10000])); - cursor = ~~next; - for (i = 0, len = found.length; i < len; i++) { - k = found[i]; - keys.push(k.slice(start, -end)); - } - } - return keys; - } - - _startAutoCleanup() { - var base; - clearInterval(this.interval); - return typeof (base = (this.interval = setInterval(async() => { - var e, k, ref, results, time, v; - time = Date.now(); - ref = this.instances; - results = []; - for (k in ref) { - v = ref[k]; - try { - if ((await v._store.__groupCheck__(time))) { - results.push(this.deleteKey(k)); - } else { - results.push(void 0); - } - } catch (error) { - e = error; - results.push(v.Events.trigger("error", e)); - } - } - return results; - }, this.timeout / 2))).unref === "function" ? base.unref() : void 0; - } - - updateSettings(options = {}) { - parser$3.overwrite(options, this.defaults, this); - parser$3.overwrite(options, options, this.limiterOptions); - if (options.timeout != null) { - return this._startAutoCleanup(); - } - } - - disconnect(flush = true) { - var ref; - if (!this.sharedConnection) { - return (ref = this.connection) != null ? ref.disconnect(flush) : void 0; - } - } - - } - Group.prototype.defaults = { - timeout: 1000 * 60 * 5, - connection: null, - Promise: Promise, - id: "group-key" - }; - - return Group; - - }).call(commonjsGlobal); - - var Group_1 = Group; - - var Batcher, Events$3, parser$4; - - parser$4 = parser; - - Events$3 = Events_1; - - Batcher = (function() { - class Batcher { - constructor(options = {}) { - this.options = options; - parser$4.load(this.options, this.defaults, this); - this.Events = new Events$3(this); - this._arr = []; - this._resetPromise(); - this._lastFlush = Date.now(); - } - - _resetPromise() { - return this._promise = new this.Promise((res, rej) => { - return this._resolve = res; - }); - } - - _flush() { - clearTimeout(this._timeout); - this._lastFlush = Date.now(); - this._resolve(); - this.Events.trigger("batch", this._arr); - this._arr = []; - return this._resetPromise(); - } - - add(data) { - var ret; - this._arr.push(data); - ret = this._promise; - if (this._arr.length === this.maxSize) { - this._flush(); - } else if ((this.maxTime != null) && this._arr.length === 1) { - this._timeout = setTimeout(() => { - return this._flush(); - }, this.maxTime); - } - return ret; - } - - } - Batcher.prototype.defaults = { - maxTime: null, - maxSize: null, - Promise: Promise - }; - - return Batcher; - - }).call(commonjsGlobal); - - var Batcher_1 = Batcher; - - var require$$4$1 = () => console.log('You must import the full version of Bottleneck in order to use this feature.'); - - var require$$8 = getCjsExportFromNamespace(version$2); - - var Bottleneck, DEFAULT_PRIORITY$1, Events$4, Job$1, LocalDatastore$1, NUM_PRIORITIES$1, Queues$1, RedisDatastore$1, States$1, Sync$1, parser$5, - splice = [].splice; - - NUM_PRIORITIES$1 = 10; - - DEFAULT_PRIORITY$1 = 5; - - parser$5 = parser; - - Queues$1 = Queues_1; - - Job$1 = Job_1; - - LocalDatastore$1 = LocalDatastore_1; - - RedisDatastore$1 = require$$4$1; - - Events$4 = Events_1; - - States$1 = States_1; - - Sync$1 = Sync_1; - - Bottleneck = (function() { - class Bottleneck { - constructor(options = {}, ...invalid) { - var storeInstanceOptions, storeOptions; - this._addToQueue = this._addToQueue.bind(this); - this._validateOptions(options, invalid); - parser$5.load(options, this.instanceDefaults, this); - this._queues = new Queues$1(NUM_PRIORITIES$1); - this._scheduled = {}; - this._states = new States$1(["RECEIVED", "QUEUED", "RUNNING", "EXECUTING"].concat(this.trackDoneStatus ? ["DONE"] : [])); - this._limiter = null; - this.Events = new Events$4(this); - this._submitLock = new Sync$1("submit", this.Promise); - this._registerLock = new Sync$1("register", this.Promise); - storeOptions = parser$5.load(options, this.storeDefaults, {}); - this._store = (function() { - if (this.datastore === "redis" || this.datastore === "ioredis" || (this.connection != null)) { - storeInstanceOptions = parser$5.load(options, this.redisStoreDefaults, {}); - return new RedisDatastore$1(this, storeOptions, storeInstanceOptions); - } else if (this.datastore === "local") { - storeInstanceOptions = parser$5.load(options, this.localStoreDefaults, {}); - return new LocalDatastore$1(this, storeOptions, storeInstanceOptions); - } else { - throw new Bottleneck.prototype.BottleneckError(`Invalid datastore type: ${this.datastore}`); - } - }).call(this); - this._queues.on("leftzero", () => { - var ref; - return (ref = this._store.heartbeat) != null ? typeof ref.ref === "function" ? ref.ref() : void 0 : void 0; - }); - this._queues.on("zero", () => { - var ref; - return (ref = this._store.heartbeat) != null ? typeof ref.unref === "function" ? ref.unref() : void 0 : void 0; - }); - } - - _validateOptions(options, invalid) { - if (!((options != null) && typeof options === "object" && invalid.length === 0)) { - throw new Bottleneck.prototype.BottleneckError("Bottleneck v2 takes a single object argument. Refer to https://github.com/SGrondin/bottleneck#upgrading-to-v2 if you're upgrading from Bottleneck v1."); - } - } - - ready() { - return this._store.ready; - } - - clients() { - return this._store.clients; - } - - channel() { - return `b_${this.id}`; - } - - channel_client() { - return `b_${this.id}_${this._store.clientId}`; - } - - publish(message) { - return this._store.__publish__(message); - } - - disconnect(flush = true) { - return this._store.__disconnect__(flush); - } - - chain(_limiter) { - this._limiter = _limiter; - return this; - } - - queued(priority) { - return this._queues.queued(priority); - } - - clusterQueued() { - return this._store.__queued__(); - } - - empty() { - return this.queued() === 0 && this._submitLock.isEmpty(); - } - - running() { - return this._store.__running__(); - } - - done() { - return this._store.__done__(); - } - - jobStatus(id) { - return this._states.jobStatus(id); - } - - jobs(status) { - return this._states.statusJobs(status); - } - - counts() { - return this._states.statusCounts(); - } - - _randomIndex() { - return Math.random().toString(36).slice(2); - } - - check(weight = 1) { - return this._store.__check__(weight); - } - - _clearGlobalState(index) { - if (this._scheduled[index] != null) { - clearTimeout(this._scheduled[index].expiration); - delete this._scheduled[index]; - return true; - } else { - return false; - } - } - - async _free(index, job, options, eventInfo) { - var e, running; - try { - ({running} = (await this._store.__free__(index, options.weight))); - this.Events.trigger("debug", `Freed ${options.id}`, eventInfo); - if (running === 0 && this.empty()) { - return this.Events.trigger("idle"); - } - } catch (error1) { - e = error1; - return this.Events.trigger("error", e); - } - } - - _run(index, job, wait) { - var clearGlobalState, free, run; - job.doRun(); - clearGlobalState = this._clearGlobalState.bind(this, index); - run = this._run.bind(this, index, job); - free = this._free.bind(this, index, job); - return this._scheduled[index] = { - timeout: setTimeout(() => { - return job.doExecute(this._limiter, clearGlobalState, run, free); - }, wait), - expiration: job.options.expiration != null ? setTimeout(function() { - return job.doExpire(clearGlobalState, run, free); - }, wait + job.options.expiration) : void 0, - job: job - }; - } - - _drainOne(capacity) { - return this._registerLock.schedule(() => { - var args, index, next, options, queue; - if (this.queued() === 0) { - return this.Promise.resolve(null); - } - queue = this._queues.getFirst(); - ({options, args} = next = queue.first()); - if ((capacity != null) && options.weight > capacity) { - return this.Promise.resolve(null); - } - this.Events.trigger("debug", `Draining ${options.id}`, {args, options}); - index = this._randomIndex(); - return this._store.__register__(index, options.weight, options.expiration).then(({success, wait, reservoir}) => { - var empty; - this.Events.trigger("debug", `Drained ${options.id}`, {success, args, options}); - if (success) { - queue.shift(); - empty = this.empty(); - if (empty) { - this.Events.trigger("empty"); - } - if (reservoir === 0) { - this.Events.trigger("depleted", empty); - } - this._run(index, next, wait); - return this.Promise.resolve(options.weight); - } else { - return this.Promise.resolve(null); - } - }); - }); - } - - _drainAll(capacity, total = 0) { - return this._drainOne(capacity).then((drained) => { - var newCapacity; - if (drained != null) { - newCapacity = capacity != null ? capacity - drained : capacity; - return this._drainAll(newCapacity, total + drained); - } else { - return this.Promise.resolve(total); - } - }).catch((e) => { - return this.Events.trigger("error", e); - }); - } - - _dropAllQueued(message) { - return this._queues.shiftAll(function(job) { - return job.doDrop({message}); - }); - } - - stop(options = {}) { - var done, waitForExecuting; - options = parser$5.load(options, this.stopDefaults); - waitForExecuting = (at) => { - var finished; - finished = () => { - var counts; - counts = this._states.counts; - return (counts[0] + counts[1] + counts[2] + counts[3]) === at; - }; - return new this.Promise((resolve, reject) => { - if (finished()) { - return resolve(); - } else { - return this.on("done", () => { - if (finished()) { - this.removeAllListeners("done"); - return resolve(); - } - }); - } - }); - }; - done = options.dropWaitingJobs ? (this._run = function(index, next) { - return next.doDrop({ - message: options.dropErrorMessage - }); - }, this._drainOne = () => { - return this.Promise.resolve(null); - }, this._registerLock.schedule(() => { - return this._submitLock.schedule(() => { - var k, ref, v; - ref = this._scheduled; - for (k in ref) { - v = ref[k]; - if (this.jobStatus(v.job.options.id) === "RUNNING") { - clearTimeout(v.timeout); - clearTimeout(v.expiration); - v.job.doDrop({ - message: options.dropErrorMessage - }); - } - } - this._dropAllQueued(options.dropErrorMessage); - return waitForExecuting(0); - }); - })) : this.schedule({ - priority: NUM_PRIORITIES$1 - 1, - weight: 0 - }, () => { - return waitForExecuting(1); - }); - this._receive = function(job) { - return job._reject(new Bottleneck.prototype.BottleneckError(options.enqueueErrorMessage)); - }; - this.stop = () => { - return this.Promise.reject(new Bottleneck.prototype.BottleneckError("stop() has already been called")); - }; - return done; - } - - async _addToQueue(job) { - var args, blocked, error, options, reachedHWM, shifted, strategy; - ({args, options} = job); - try { - ({reachedHWM, blocked, strategy} = (await this._store.__submit__(this.queued(), options.weight))); - } catch (error1) { - error = error1; - this.Events.trigger("debug", `Could not queue ${options.id}`, {args, options, error}); - job.doDrop({error}); - return false; - } - if (blocked) { - job.doDrop(); - return true; - } else if (reachedHWM) { - shifted = strategy === Bottleneck.prototype.strategy.LEAK ? this._queues.shiftLastFrom(options.priority) : strategy === Bottleneck.prototype.strategy.OVERFLOW_PRIORITY ? this._queues.shiftLastFrom(options.priority + 1) : strategy === Bottleneck.prototype.strategy.OVERFLOW ? job : void 0; - if (shifted != null) { - shifted.doDrop(); - } - if ((shifted == null) || strategy === Bottleneck.prototype.strategy.OVERFLOW) { - if (shifted == null) { - job.doDrop(); - } - return reachedHWM; - } - } - job.doQueue(reachedHWM, blocked); - this._queues.push(job); - await this._drainAll(); - return reachedHWM; - } - - _receive(job) { - if (this._states.jobStatus(job.options.id) != null) { - job._reject(new Bottleneck.prototype.BottleneckError(`A job with the same id already exists (id=${job.options.id})`)); - return false; - } else { - job.doReceive(); - return this._submitLock.schedule(this._addToQueue, job); - } - } - - submit(...args) { - var cb, fn, job, options, ref, ref1, task; - if (typeof args[0] === "function") { - ref = args, [fn, ...args] = ref, [cb] = splice.call(args, -1); - options = parser$5.load({}, this.jobDefaults); - } else { - ref1 = args, [options, fn, ...args] = ref1, [cb] = splice.call(args, -1); - options = parser$5.load(options, this.jobDefaults); - } - task = (...args) => { - return new this.Promise(function(resolve, reject) { - return fn(...args, function(...args) { - return (args[0] != null ? reject : resolve)(args); - }); - }); - }; - job = new Job$1(task, args, options, this.jobDefaults, this.rejectOnDrop, this.Events, this._states, this.Promise); - job.promise.then(function(args) { - return typeof cb === "function" ? cb(...args) : void 0; - }).catch(function(args) { - if (Array.isArray(args)) { - return typeof cb === "function" ? cb(...args) : void 0; - } else { - return typeof cb === "function" ? cb(args) : void 0; - } - }); - return this._receive(job); - } - - schedule(...args) { - var job, options, task; - if (typeof args[0] === "function") { - [task, ...args] = args; - options = {}; - } else { - [options, task, ...args] = args; - } - job = new Job$1(task, args, options, this.jobDefaults, this.rejectOnDrop, this.Events, this._states, this.Promise); - this._receive(job); - return job.promise; - } - - wrap(fn) { - var schedule, wrapped; - schedule = this.schedule.bind(this); - wrapped = function(...args) { - return schedule(fn.bind(this), ...args); - }; - wrapped.withOptions = function(options, ...args) { - return schedule(options, fn, ...args); - }; - return wrapped; - } - - async updateSettings(options = {}) { - await this._store.__updateSettings__(parser$5.overwrite(options, this.storeDefaults)); - parser$5.overwrite(options, this.instanceDefaults, this); - return this; - } - - currentReservoir() { - return this._store.__currentReservoir__(); - } - - incrementReservoir(incr = 0) { - return this._store.__incrementReservoir__(incr); - } - - } - Bottleneck.default = Bottleneck; - - Bottleneck.Events = Events$4; - - Bottleneck.version = Bottleneck.prototype.version = require$$8.version; - - Bottleneck.strategy = Bottleneck.prototype.strategy = { - LEAK: 1, - OVERFLOW: 2, - OVERFLOW_PRIORITY: 4, - BLOCK: 3 - }; - - Bottleneck.BottleneckError = Bottleneck.prototype.BottleneckError = BottleneckError_1; - - Bottleneck.Group = Bottleneck.prototype.Group = Group_1; - - Bottleneck.RedisConnection = Bottleneck.prototype.RedisConnection = require$$2; - - Bottleneck.IORedisConnection = Bottleneck.prototype.IORedisConnection = require$$3; - - Bottleneck.Batcher = Bottleneck.prototype.Batcher = Batcher_1; - - Bottleneck.prototype.jobDefaults = { - priority: DEFAULT_PRIORITY$1, - weight: 1, - expiration: null, - id: "" - }; - - Bottleneck.prototype.storeDefaults = { - maxConcurrent: null, - minTime: 0, - highWater: null, - strategy: Bottleneck.prototype.strategy.LEAK, - penalty: null, - reservoir: null, - reservoirRefreshInterval: null, - reservoirRefreshAmount: null, - reservoirIncreaseInterval: null, - reservoirIncreaseAmount: null, - reservoirIncreaseMaximum: null - }; - - Bottleneck.prototype.localStoreDefaults = { - Promise: Promise, - timeout: null, - heartbeatInterval: 250 - }; - - Bottleneck.prototype.redisStoreDefaults = { - Promise: Promise, - timeout: null, - heartbeatInterval: 5000, - clientTimeout: 10000, - Redis: null, - clientOptions: {}, - clusterNodes: null, - clearDatastore: false, - connection: null - }; - - Bottleneck.prototype.instanceDefaults = { - datastore: "local", - connection: null, - id: "", - rejectOnDrop: true, - trackDoneStatus: false, - Promise: Promise - }; - - Bottleneck.prototype.stopDefaults = { - enqueueErrorMessage: "This limiter has been stopped and cannot accept new jobs.", - dropWaitingJobs: true, - dropErrorMessage: "This limiter has been stopped." - }; - - return Bottleneck; - - }).call(commonjsGlobal); - - var Bottleneck_1 = Bottleneck; - - var lib = Bottleneck_1; - - return lib; - -}))); - - -/***/ }), - -/***/ 8932: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -class Deprecation extends Error { - constructor(message) { - super(message); // Maintains proper stack trace (only available on V8) - - /* istanbul ignore next */ - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); - } - - this.name = 'Deprecation'; - } - -} - -exports.Deprecation = Deprecation; - - -/***/ }), - -/***/ 3287: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -/*! - * is-plain-object - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - -function isObject(o) { - return Object.prototype.toString.call(o) === '[object Object]'; -} - -function isPlainObject(o) { - var ctor,prot; - - if (isObject(o) === false) return false; - - // If has modified constructor - ctor = o.constructor; - if (ctor === undefined) return true; - - // If has modified prototype - prot = ctor.prototype; - if (isObject(prot) === false) return false; - - // If constructor does not have an Object-specific method - if (prot.hasOwnProperty('isPrototypeOf') === false) { - return false; - } - - // Most likely a plain Object - return true; -} - -exports.isPlainObject = isPlainObject; - - -/***/ }), - -/***/ 467: -/***/ ((module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } - -var Stream = _interopDefault(__nccwpck_require__(2781)); -var http = _interopDefault(__nccwpck_require__(3685)); -var Url = _interopDefault(__nccwpck_require__(7310)); -var whatwgUrl = _interopDefault(__nccwpck_require__(8665)); -var https = _interopDefault(__nccwpck_require__(5687)); -var zlib = _interopDefault(__nccwpck_require__(9796)); - -// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js - -// fix for "Readable" isn't a named export issue -const Readable = Stream.Readable; - -const BUFFER = Symbol('buffer'); -const TYPE = Symbol('type'); - -class Blob { - constructor() { - this[TYPE] = ''; - - const blobParts = arguments[0]; - const options = arguments[1]; - - const buffers = []; - let size = 0; - - if (blobParts) { - const a = blobParts; - const length = Number(a.length); - for (let i = 0; i < length; i++) { - const element = a[i]; - let buffer; - if (element instanceof Buffer) { - buffer = element; - } else if (ArrayBuffer.isView(element)) { - buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength); - } else if (element instanceof ArrayBuffer) { - buffer = Buffer.from(element); - } else if (element instanceof Blob) { - buffer = element[BUFFER]; - } else { - buffer = Buffer.from(typeof element === 'string' ? element : String(element)); - } - size += buffer.length; - buffers.push(buffer); - } - } - - this[BUFFER] = Buffer.concat(buffers); - - let type = options && options.type !== undefined && String(options.type).toLowerCase(); - if (type && !/[^\u0020-\u007E]/.test(type)) { - this[TYPE] = type; - } - } - get size() { - return this[BUFFER].length; - } - get type() { - return this[TYPE]; - } - text() { - return Promise.resolve(this[BUFFER].toString()); - } - arrayBuffer() { - const buf = this[BUFFER]; - const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); - return Promise.resolve(ab); - } - stream() { - const readable = new Readable(); - readable._read = function () {}; - readable.push(this[BUFFER]); - readable.push(null); - return readable; - } - toString() { - return '[object Blob]'; - } - slice() { - const size = this.size; - - const start = arguments[0]; - const end = arguments[1]; - let relativeStart, relativeEnd; - if (start === undefined) { - relativeStart = 0; - } else if (start < 0) { - relativeStart = Math.max(size + start, 0); - } else { - relativeStart = Math.min(start, size); - } - if (end === undefined) { - relativeEnd = size; - } else if (end < 0) { - relativeEnd = Math.max(size + end, 0); - } else { - relativeEnd = Math.min(end, size); - } - const span = Math.max(relativeEnd - relativeStart, 0); - - const buffer = this[BUFFER]; - const slicedBuffer = buffer.slice(relativeStart, relativeStart + span); - const blob = new Blob([], { type: arguments[2] }); - blob[BUFFER] = slicedBuffer; - return blob; - } -} - -Object.defineProperties(Blob.prototype, { - size: { enumerable: true }, - type: { enumerable: true }, - slice: { enumerable: true } -}); - -Object.defineProperty(Blob.prototype, Symbol.toStringTag, { - value: 'Blob', - writable: false, - enumerable: false, - configurable: true -}); - -/** - * fetch-error.js - * - * FetchError interface for operational errors - */ - -/** - * Create FetchError instance - * - * @param String message Error message for human - * @param String type Error type for machine - * @param String systemError For Node.js system error - * @return FetchError - */ -function FetchError(message, type, systemError) { - Error.call(this, message); - - this.message = message; - this.type = type; - - // when err.type is `system`, err.code contains system error code - if (systemError) { - this.code = this.errno = systemError.code; - } - - // hide custom error implementation details from end-users - Error.captureStackTrace(this, this.constructor); -} - -FetchError.prototype = Object.create(Error.prototype); -FetchError.prototype.constructor = FetchError; -FetchError.prototype.name = 'FetchError'; - -let convert; -try { - convert = (__nccwpck_require__(3975).convert); -} catch (e) {} - -const INTERNALS = Symbol('Body internals'); - -// fix an issue where "PassThrough" isn't a named export for node <10 -const PassThrough = Stream.PassThrough; - -/** - * Body mixin - * - * Ref: https://fetch.spec.whatwg.org/#body - * - * @param Stream body Readable stream - * @param Object opts Response options - * @return Void - */ -function Body(body) { - var _this = this; - - var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, - _ref$size = _ref.size; - - let size = _ref$size === undefined ? 0 : _ref$size; - var _ref$timeout = _ref.timeout; - let timeout = _ref$timeout === undefined ? 0 : _ref$timeout; - - if (body == null) { - // body is undefined or null - body = null; - } else if (isURLSearchParams(body)) { - // body is a URLSearchParams - body = Buffer.from(body.toString()); - } else if (isBlob(body)) ; else if (Buffer.isBuffer(body)) ; else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') { - // body is ArrayBuffer - body = Buffer.from(body); - } else if (ArrayBuffer.isView(body)) { - // body is ArrayBufferView - body = Buffer.from(body.buffer, body.byteOffset, body.byteLength); - } else if (body instanceof Stream) ; else { - // none of the above - // coerce to string then buffer - body = Buffer.from(String(body)); - } - this[INTERNALS] = { - body, - disturbed: false, - error: null - }; - this.size = size; - this.timeout = timeout; - - if (body instanceof Stream) { - body.on('error', function (err) { - const error = err.name === 'AbortError' ? err : new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err); - _this[INTERNALS].error = error; - }); - } -} - -Body.prototype = { - get body() { - return this[INTERNALS].body; - }, - - get bodyUsed() { - return this[INTERNALS].disturbed; - }, - - /** - * Decode response as ArrayBuffer - * - * @return Promise - */ - arrayBuffer() { - return consumeBody.call(this).then(function (buf) { - return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); - }); - }, - - /** - * Return raw response as Blob - * - * @return Promise - */ - blob() { - let ct = this.headers && this.headers.get('content-type') || ''; - return consumeBody.call(this).then(function (buf) { - return Object.assign( - // Prevent copying - new Blob([], { - type: ct.toLowerCase() - }), { - [BUFFER]: buf - }); - }); - }, - - /** - * Decode response as json - * - * @return Promise - */ - json() { - var _this2 = this; - - return consumeBody.call(this).then(function (buffer) { - try { - return JSON.parse(buffer.toString()); - } catch (err) { - return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json')); - } - }); - }, - - /** - * Decode response as text - * - * @return Promise - */ - text() { - return consumeBody.call(this).then(function (buffer) { - return buffer.toString(); - }); - }, - - /** - * Decode response as buffer (non-spec api) - * - * @return Promise - */ - buffer() { - return consumeBody.call(this); - }, - - /** - * Decode response as text, while automatically detecting the encoding and - * trying to decode to UTF-8 (non-spec api) - * - * @return Promise - */ - textConverted() { - var _this3 = this; - - return consumeBody.call(this).then(function (buffer) { - return convertBody(buffer, _this3.headers); - }); - } -}; - -// In browsers, all properties are enumerable. -Object.defineProperties(Body.prototype, { - body: { enumerable: true }, - bodyUsed: { enumerable: true }, - arrayBuffer: { enumerable: true }, - blob: { enumerable: true }, - json: { enumerable: true }, - text: { enumerable: true } -}); - -Body.mixIn = function (proto) { - for (const name of Object.getOwnPropertyNames(Body.prototype)) { - // istanbul ignore else: future proof - if (!(name in proto)) { - const desc = Object.getOwnPropertyDescriptor(Body.prototype, name); - Object.defineProperty(proto, name, desc); - } - } -}; - -/** - * Consume and convert an entire Body to a Buffer. - * - * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body - * - * @return Promise - */ -function consumeBody() { - var _this4 = this; - - if (this[INTERNALS].disturbed) { - return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`)); - } - - this[INTERNALS].disturbed = true; - - if (this[INTERNALS].error) { - return Body.Promise.reject(this[INTERNALS].error); - } - - let body = this.body; - - // body is null - if (body === null) { - return Body.Promise.resolve(Buffer.alloc(0)); - } - - // body is blob - if (isBlob(body)) { - body = body.stream(); - } - - // body is buffer - if (Buffer.isBuffer(body)) { - return Body.Promise.resolve(body); - } - - // istanbul ignore if: should never happen - if (!(body instanceof Stream)) { - return Body.Promise.resolve(Buffer.alloc(0)); - } - - // body is stream - // get ready to actually consume the body - let accum = []; - let accumBytes = 0; - let abort = false; - - return new Body.Promise(function (resolve, reject) { - let resTimeout; - - // allow timeout on slow response body - if (_this4.timeout) { - resTimeout = setTimeout(function () { - abort = true; - reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout')); - }, _this4.timeout); - } - - // handle stream errors - body.on('error', function (err) { - if (err.name === 'AbortError') { - // if the request was aborted, reject with this Error - abort = true; - reject(err); - } else { - // other errors, such as incorrect content-encoding - reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err)); - } - }); - - body.on('data', function (chunk) { - if (abort || chunk === null) { - return; - } - - if (_this4.size && accumBytes + chunk.length > _this4.size) { - abort = true; - reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size')); - return; - } - - accumBytes += chunk.length; - accum.push(chunk); - }); - - body.on('end', function () { - if (abort) { - return; - } - - clearTimeout(resTimeout); - - try { - resolve(Buffer.concat(accum, accumBytes)); - } catch (err) { - // handle streams that have accumulated too much data (issue #414) - reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err)); - } - }); - }); -} - -/** - * Detect buffer encoding and convert to target encoding - * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding - * - * @param Buffer buffer Incoming buffer - * @param String encoding Target encoding - * @return String - */ -function convertBody(buffer, headers) { - if (typeof convert !== 'function') { - throw new Error('The package `encoding` must be installed to use the textConverted() function'); - } - - const ct = headers.get('content-type'); - let charset = 'utf-8'; - let res, str; - - // header - if (ct) { - res = /charset=([^;]*)/i.exec(ct); - } - - // no charset in content type, peek at response body for at most 1024 bytes - str = buffer.slice(0, 1024).toString(); - - // html5 - if (!res && str) { - res = / 0 && arguments[0] !== undefined ? arguments[0] : undefined; - - this[MAP] = Object.create(null); - - if (init instanceof Headers) { - const rawHeaders = init.raw(); - const headerNames = Object.keys(rawHeaders); - - for (const headerName of headerNames) { - for (const value of rawHeaders[headerName]) { - this.append(headerName, value); - } - } - - return; - } - - // We don't worry about converting prop to ByteString here as append() - // will handle it. - if (init == null) ; else if (typeof init === 'object') { - const method = init[Symbol.iterator]; - if (method != null) { - if (typeof method !== 'function') { - throw new TypeError('Header pairs must be iterable'); - } - - // sequence> - // Note: per spec we have to first exhaust the lists then process them - const pairs = []; - for (const pair of init) { - if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') { - throw new TypeError('Each header pair must be iterable'); - } - pairs.push(Array.from(pair)); - } - - for (const pair of pairs) { - if (pair.length !== 2) { - throw new TypeError('Each header pair must be a name/value tuple'); - } - this.append(pair[0], pair[1]); - } - } else { - // record - for (const key of Object.keys(init)) { - const value = init[key]; - this.append(key, value); - } - } - } else { - throw new TypeError('Provided initializer must be an object'); - } - } - - /** - * Return combined header value given name - * - * @param String name Header name - * @return Mixed - */ - get(name) { - name = `${name}`; - validateName(name); - const key = find(this[MAP], name); - if (key === undefined) { - return null; - } - - return this[MAP][key].join(', '); - } - - /** - * Iterate over all headers - * - * @param Function callback Executed for each item with parameters (value, name, thisArg) - * @param Boolean thisArg `this` context for callback function - * @return Void - */ - forEach(callback) { - let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined; - - let pairs = getHeaders(this); - let i = 0; - while (i < pairs.length) { - var _pairs$i = pairs[i]; - const name = _pairs$i[0], - value = _pairs$i[1]; - - callback.call(thisArg, value, name, this); - pairs = getHeaders(this); - i++; - } - } - - /** - * Overwrite header values given name - * - * @param String name Header name - * @param String value Header value - * @return Void - */ - set(name, value) { - name = `${name}`; - value = `${value}`; - validateName(name); - validateValue(value); - const key = find(this[MAP], name); - this[MAP][key !== undefined ? key : name] = [value]; - } - - /** - * Append a value onto existing header - * - * @param String name Header name - * @param String value Header value - * @return Void - */ - append(name, value) { - name = `${name}`; - value = `${value}`; - validateName(name); - validateValue(value); - const key = find(this[MAP], name); - if (key !== undefined) { - this[MAP][key].push(value); - } else { - this[MAP][name] = [value]; - } - } - - /** - * Check for header name existence - * - * @param String name Header name - * @return Boolean - */ - has(name) { - name = `${name}`; - validateName(name); - return find(this[MAP], name) !== undefined; - } - - /** - * Delete all header values given name - * - * @param String name Header name - * @return Void - */ - delete(name) { - name = `${name}`; - validateName(name); - const key = find(this[MAP], name); - if (key !== undefined) { - delete this[MAP][key]; - } - } - - /** - * Return raw headers (non-spec api) - * - * @return Object - */ - raw() { - return this[MAP]; - } - - /** - * Get an iterator on keys. - * - * @return Iterator - */ - keys() { - return createHeadersIterator(this, 'key'); - } - - /** - * Get an iterator on values. - * - * @return Iterator - */ - values() { - return createHeadersIterator(this, 'value'); - } - - /** - * Get an iterator on entries. - * - * This is the default iterator of the Headers object. - * - * @return Iterator - */ - [Symbol.iterator]() { - return createHeadersIterator(this, 'key+value'); - } -} -Headers.prototype.entries = Headers.prototype[Symbol.iterator]; - -Object.defineProperty(Headers.prototype, Symbol.toStringTag, { - value: 'Headers', - writable: false, - enumerable: false, - configurable: true -}); - -Object.defineProperties(Headers.prototype, { - get: { enumerable: true }, - forEach: { enumerable: true }, - set: { enumerable: true }, - append: { enumerable: true }, - has: { enumerable: true }, - delete: { enumerable: true }, - keys: { enumerable: true }, - values: { enumerable: true }, - entries: { enumerable: true } -}); - -function getHeaders(headers) { - let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value'; - - const keys = Object.keys(headers[MAP]).sort(); - return keys.map(kind === 'key' ? function (k) { - return k.toLowerCase(); - } : kind === 'value' ? function (k) { - return headers[MAP][k].join(', '); - } : function (k) { - return [k.toLowerCase(), headers[MAP][k].join(', ')]; - }); -} - -const INTERNAL = Symbol('internal'); - -function createHeadersIterator(target, kind) { - const iterator = Object.create(HeadersIteratorPrototype); - iterator[INTERNAL] = { - target, - kind, - index: 0 - }; - return iterator; -} - -const HeadersIteratorPrototype = Object.setPrototypeOf({ - next() { - // istanbul ignore if - if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) { - throw new TypeError('Value of `this` is not a HeadersIterator'); - } - - var _INTERNAL = this[INTERNAL]; - const target = _INTERNAL.target, - kind = _INTERNAL.kind, - index = _INTERNAL.index; - - const values = getHeaders(target, kind); - const len = values.length; - if (index >= len) { - return { - value: undefined, - done: true - }; - } - - this[INTERNAL].index = index + 1; - - return { - value: values[index], - done: false - }; - } -}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()))); - -Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, { - value: 'HeadersIterator', - writable: false, - enumerable: false, - configurable: true -}); - -/** - * Export the Headers object in a form that Node.js can consume. - * - * @param Headers headers - * @return Object - */ -function exportNodeCompatibleHeaders(headers) { - const obj = Object.assign({ __proto__: null }, headers[MAP]); - - // http.request() only supports string as Host header. This hack makes - // specifying custom Host header possible. - const hostHeaderKey = find(headers[MAP], 'Host'); - if (hostHeaderKey !== undefined) { - obj[hostHeaderKey] = obj[hostHeaderKey][0]; - } - - return obj; -} - -/** - * Create a Headers object from an object of headers, ignoring those that do - * not conform to HTTP grammar productions. - * - * @param Object obj Object of headers - * @return Headers - */ -function createHeadersLenient(obj) { - const headers = new Headers(); - for (const name of Object.keys(obj)) { - if (invalidTokenRegex.test(name)) { - continue; - } - if (Array.isArray(obj[name])) { - for (const val of obj[name]) { - if (invalidHeaderCharRegex.test(val)) { - continue; - } - if (headers[MAP][name] === undefined) { - headers[MAP][name] = [val]; - } else { - headers[MAP][name].push(val); - } - } - } else if (!invalidHeaderCharRegex.test(obj[name])) { - headers[MAP][name] = [obj[name]]; - } - } - return headers; -} - -const INTERNALS$1 = Symbol('Response internals'); - -// fix an issue where "STATUS_CODES" aren't a named export for node <10 -const STATUS_CODES = http.STATUS_CODES; - -/** - * Response class - * - * @param Stream body Readable stream - * @param Object opts Response options - * @return Void - */ -class Response { - constructor() { - let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - Body.call(this, body, opts); - - const status = opts.status || 200; - const headers = new Headers(opts.headers); - - if (body != null && !headers.has('Content-Type')) { - const contentType = extractContentType(body); - if (contentType) { - headers.append('Content-Type', contentType); - } - } - - this[INTERNALS$1] = { - url: opts.url, - status, - statusText: opts.statusText || STATUS_CODES[status], - headers, - counter: opts.counter - }; - } - - get url() { - return this[INTERNALS$1].url || ''; - } - - get status() { - return this[INTERNALS$1].status; - } - - /** - * Convenience property representing if the request ended normally - */ - get ok() { - return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; - } - - get redirected() { - return this[INTERNALS$1].counter > 0; - } - - get statusText() { - return this[INTERNALS$1].statusText; - } - - get headers() { - return this[INTERNALS$1].headers; - } - - /** - * Clone this response - * - * @return Response - */ - clone() { - return new Response(clone(this), { - url: this.url, - status: this.status, - statusText: this.statusText, - headers: this.headers, - ok: this.ok, - redirected: this.redirected - }); - } -} - -Body.mixIn(Response.prototype); - -Object.defineProperties(Response.prototype, { - url: { enumerable: true }, - status: { enumerable: true }, - ok: { enumerable: true }, - redirected: { enumerable: true }, - statusText: { enumerable: true }, - headers: { enumerable: true }, - clone: { enumerable: true } -}); - -Object.defineProperty(Response.prototype, Symbol.toStringTag, { - value: 'Response', - writable: false, - enumerable: false, - configurable: true -}); - -const INTERNALS$2 = Symbol('Request internals'); -const URL = Url.URL || whatwgUrl.URL; - -// fix an issue where "format", "parse" aren't a named export for node <10 -const parse_url = Url.parse; -const format_url = Url.format; - -/** - * Wrapper around `new URL` to handle arbitrary URLs - * - * @param {string} urlStr - * @return {void} - */ -function parseURL(urlStr) { - /* - Check whether the URL is absolute or not - Scheme: https://tools.ietf.org/html/rfc3986#section-3.1 - Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3 - */ - if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.exec(urlStr)) { - urlStr = new URL(urlStr).toString(); - } - - // Fallback to old implementation for arbitrary URLs - return parse_url(urlStr); -} - -const streamDestructionSupported = 'destroy' in Stream.Readable.prototype; - -/** - * Check if a value is an instance of Request. - * - * @param Mixed input - * @return Boolean - */ -function isRequest(input) { - return typeof input === 'object' && typeof input[INTERNALS$2] === 'object'; -} - -function isAbortSignal(signal) { - const proto = signal && typeof signal === 'object' && Object.getPrototypeOf(signal); - return !!(proto && proto.constructor.name === 'AbortSignal'); -} - -/** - * Request class - * - * @param Mixed input Url or Request instance - * @param Object init Custom options - * @return Void - */ -class Request { - constructor(input) { - let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - - let parsedURL; - - // normalize input - if (!isRequest(input)) { - if (input && input.href) { - // in order to support Node.js' Url objects; though WHATWG's URL objects - // will fall into this branch also (since their `toString()` will return - // `href` property anyway) - parsedURL = parseURL(input.href); - } else { - // coerce input to a string before attempting to parse - parsedURL = parseURL(`${input}`); - } - input = {}; - } else { - parsedURL = parseURL(input.url); - } - - let method = init.method || input.method || 'GET'; - method = method.toUpperCase(); - - if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) { - throw new TypeError('Request with GET/HEAD method cannot have body'); - } - - let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null; - - Body.call(this, inputBody, { - timeout: init.timeout || input.timeout || 0, - size: init.size || input.size || 0 - }); - - const headers = new Headers(init.headers || input.headers || {}); - - if (inputBody != null && !headers.has('Content-Type')) { - const contentType = extractContentType(inputBody); - if (contentType) { - headers.append('Content-Type', contentType); - } - } - - let signal = isRequest(input) ? input.signal : null; - if ('signal' in init) signal = init.signal; - - if (signal != null && !isAbortSignal(signal)) { - throw new TypeError('Expected signal to be an instanceof AbortSignal'); - } - - this[INTERNALS$2] = { - method, - redirect: init.redirect || input.redirect || 'follow', - headers, - parsedURL, - signal - }; - - // node-fetch-only options - this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20; - this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true; - this.counter = init.counter || input.counter || 0; - this.agent = init.agent || input.agent; - } - - get method() { - return this[INTERNALS$2].method; - } - - get url() { - return format_url(this[INTERNALS$2].parsedURL); - } - - get headers() { - return this[INTERNALS$2].headers; - } - - get redirect() { - return this[INTERNALS$2].redirect; - } - - get signal() { - return this[INTERNALS$2].signal; - } - - /** - * Clone this request - * - * @return Request - */ - clone() { - return new Request(this); - } -} - -Body.mixIn(Request.prototype); - -Object.defineProperty(Request.prototype, Symbol.toStringTag, { - value: 'Request', - writable: false, - enumerable: false, - configurable: true -}); - -Object.defineProperties(Request.prototype, { - method: { enumerable: true }, - url: { enumerable: true }, - headers: { enumerable: true }, - redirect: { enumerable: true }, - clone: { enumerable: true }, - signal: { enumerable: true } -}); - -/** - * Convert a Request to Node.js http request options. - * - * @param Request A Request instance - * @return Object The options object to be passed to http.request - */ -function getNodeRequestOptions(request) { - const parsedURL = request[INTERNALS$2].parsedURL; - const headers = new Headers(request[INTERNALS$2].headers); - - // fetch step 1.3 - if (!headers.has('Accept')) { - headers.set('Accept', '*/*'); - } - - // Basic fetch - if (!parsedURL.protocol || !parsedURL.hostname) { - throw new TypeError('Only absolute URLs are supported'); - } - - if (!/^https?:$/.test(parsedURL.protocol)) { - throw new TypeError('Only HTTP(S) protocols are supported'); - } - - if (request.signal && request.body instanceof Stream.Readable && !streamDestructionSupported) { - throw new Error('Cancellation of streamed requests with AbortSignal is not supported in node < 8'); - } - - // HTTP-network-or-cache fetch steps 2.4-2.7 - let contentLengthValue = null; - if (request.body == null && /^(POST|PUT)$/i.test(request.method)) { - contentLengthValue = '0'; - } - if (request.body != null) { - const totalBytes = getTotalBytes(request); - if (typeof totalBytes === 'number') { - contentLengthValue = String(totalBytes); - } - } - if (contentLengthValue) { - headers.set('Content-Length', contentLengthValue); - } - - // HTTP-network-or-cache fetch step 2.11 - if (!headers.has('User-Agent')) { - headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)'); - } - - // HTTP-network-or-cache fetch step 2.15 - if (request.compress && !headers.has('Accept-Encoding')) { - headers.set('Accept-Encoding', 'gzip,deflate'); - } - - let agent = request.agent; - if (typeof agent === 'function') { - agent = agent(parsedURL); - } - - if (!headers.has('Connection') && !agent) { - headers.set('Connection', 'close'); - } - - // HTTP-network fetch step 4.2 - // chunked encoding is handled by Node.js - - return Object.assign({}, parsedURL, { - method: request.method, - headers: exportNodeCompatibleHeaders(headers), - agent - }); -} - -/** - * abort-error.js - * - * AbortError interface for cancelled requests - */ - -/** - * Create AbortError instance - * - * @param String message Error message for human - * @return AbortError - */ -function AbortError(message) { - Error.call(this, message); - - this.type = 'aborted'; - this.message = message; - - // hide custom error implementation details from end-users - Error.captureStackTrace(this, this.constructor); -} - -AbortError.prototype = Object.create(Error.prototype); -AbortError.prototype.constructor = AbortError; -AbortError.prototype.name = 'AbortError'; - -const URL$1 = Url.URL || whatwgUrl.URL; - -// fix an issue where "PassThrough", "resolve" aren't a named export for node <10 -const PassThrough$1 = Stream.PassThrough; - -const isDomainOrSubdomain = function isDomainOrSubdomain(destination, original) { - const orig = new URL$1(original).hostname; - const dest = new URL$1(destination).hostname; - - return orig === dest || orig[orig.length - dest.length - 1] === '.' && orig.endsWith(dest); -}; - -/** - * Fetch function - * - * @param Mixed url Absolute url or Request instance - * @param Object opts Fetch options - * @return Promise - */ -function fetch(url, opts) { - - // allow custom promise - if (!fetch.Promise) { - throw new Error('native promise missing, set fetch.Promise to your favorite alternative'); - } - - Body.Promise = fetch.Promise; - - // wrap http.request into fetch - return new fetch.Promise(function (resolve, reject) { - // build request object - const request = new Request(url, opts); - const options = getNodeRequestOptions(request); - - const send = (options.protocol === 'https:' ? https : http).request; - const signal = request.signal; - - let response = null; - - const abort = function abort() { - let error = new AbortError('The user aborted a request.'); - reject(error); - if (request.body && request.body instanceof Stream.Readable) { - request.body.destroy(error); - } - if (!response || !response.body) return; - response.body.emit('error', error); - }; - - if (signal && signal.aborted) { - abort(); - return; - } - - const abortAndFinalize = function abortAndFinalize() { - abort(); - finalize(); - }; - - // send request - const req = send(options); - let reqTimeout; - - if (signal) { - signal.addEventListener('abort', abortAndFinalize); - } - - function finalize() { - req.abort(); - if (signal) signal.removeEventListener('abort', abortAndFinalize); - clearTimeout(reqTimeout); - } - - if (request.timeout) { - req.once('socket', function (socket) { - reqTimeout = setTimeout(function () { - reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout')); - finalize(); - }, request.timeout); - }); - } - - req.on('error', function (err) { - reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err)); - finalize(); - }); - - req.on('response', function (res) { - clearTimeout(reqTimeout); - - const headers = createHeadersLenient(res.headers); - - // HTTP fetch step 5 - if (fetch.isRedirect(res.statusCode)) { - // HTTP fetch step 5.2 - const location = headers.get('Location'); - - // HTTP fetch step 5.3 - let locationURL = null; - try { - locationURL = location === null ? null : new URL$1(location, request.url).toString(); - } catch (err) { - // error here can only be invalid URL in Location: header - // do not throw when options.redirect == manual - // let the user extract the errorneous redirect URL - if (request.redirect !== 'manual') { - reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, 'invalid-redirect')); - finalize(); - return; - } - } - - // HTTP fetch step 5.5 - switch (request.redirect) { - case 'error': - reject(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, 'no-redirect')); - finalize(); - return; - case 'manual': - // node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL. - if (locationURL !== null) { - // handle corrupted header - try { - headers.set('Location', locationURL); - } catch (err) { - // istanbul ignore next: nodejs server prevent invalid response headers, we can't test this through normal request - reject(err); - } - } - break; - case 'follow': - // HTTP-redirect fetch step 2 - if (locationURL === null) { - break; - } - - // HTTP-redirect fetch step 5 - if (request.counter >= request.follow) { - reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); - finalize(); - return; - } - - // HTTP-redirect fetch step 6 (counter increment) - // Create a new Request object. - const requestOpts = { - headers: new Headers(request.headers), - follow: request.follow, - counter: request.counter + 1, - agent: request.agent, - compress: request.compress, - method: request.method, - body: request.body, - signal: request.signal, - timeout: request.timeout, - size: request.size - }; - - if (!isDomainOrSubdomain(request.url, locationURL)) { - for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { - requestOpts.headers.delete(name); - } - } - - // HTTP-redirect fetch step 9 - if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) { - reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); - finalize(); - return; - } - - // HTTP-redirect fetch step 11 - if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') { - requestOpts.method = 'GET'; - requestOpts.body = undefined; - requestOpts.headers.delete('content-length'); - } - - // HTTP-redirect fetch step 15 - resolve(fetch(new Request(locationURL, requestOpts))); - finalize(); - return; - } - } - - // prepare response - res.once('end', function () { - if (signal) signal.removeEventListener('abort', abortAndFinalize); - }); - let body = res.pipe(new PassThrough$1()); - - const response_options = { - url: request.url, - status: res.statusCode, - statusText: res.statusMessage, - headers: headers, - size: request.size, - timeout: request.timeout, - counter: request.counter - }; - - // HTTP-network fetch step 12.1.1.3 - const codings = headers.get('Content-Encoding'); - - // HTTP-network fetch step 12.1.1.4: handle content codings - - // in following scenarios we ignore compression support - // 1. compression support is disabled - // 2. HEAD request - // 3. no Content-Encoding header - // 4. no content response (204) - // 5. content not modified response (304) - if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) { - response = new Response(body, response_options); - resolve(response); - return; - } - - // For Node v6+ - // Be less strict when decoding compressed responses, since sometimes - // servers send slightly invalid responses that are still accepted - // by common browsers. - // Always using Z_SYNC_FLUSH is what cURL does. - const zlibOptions = { - flush: zlib.Z_SYNC_FLUSH, - finishFlush: zlib.Z_SYNC_FLUSH - }; - - // for gzip - if (codings == 'gzip' || codings == 'x-gzip') { - body = body.pipe(zlib.createGunzip(zlibOptions)); - response = new Response(body, response_options); - resolve(response); - return; - } - - // for deflate - if (codings == 'deflate' || codings == 'x-deflate') { - // handle the infamous raw deflate response from old servers - // a hack for old IIS and Apache servers - const raw = res.pipe(new PassThrough$1()); - raw.once('data', function (chunk) { - // see http://stackoverflow.com/questions/37519828 - if ((chunk[0] & 0x0F) === 0x08) { - body = body.pipe(zlib.createInflate()); - } else { - body = body.pipe(zlib.createInflateRaw()); - } - response = new Response(body, response_options); - resolve(response); - }); - return; - } - - // for br - if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') { - body = body.pipe(zlib.createBrotliDecompress()); - response = new Response(body, response_options); - resolve(response); - return; - } - - // otherwise, use response as-is - response = new Response(body, response_options); - resolve(response); - }); - - writeToStream(req, request); - }); -} -/** - * Redirect code matching - * - * @param Number code Status code - * @return Boolean - */ -fetch.isRedirect = function (code) { - return code === 301 || code === 302 || code === 303 || code === 307 || code === 308; -}; - -// expose Promise -fetch.Promise = global.Promise; - -module.exports = exports = fetch; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports["default"] = exports; -exports.Headers = Headers; -exports.Request = Request; -exports.Response = Response; -exports.FetchError = FetchError; - - -/***/ }), - -/***/ 1223: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -var wrappy = __nccwpck_require__(2940) -module.exports = wrappy(once) -module.exports.strict = wrappy(onceStrict) - -once.proto = once(function () { - Object.defineProperty(Function.prototype, 'once', { - value: function () { - return once(this) - }, - configurable: true - }) - - Object.defineProperty(Function.prototype, 'onceStrict', { - value: function () { - return onceStrict(this) - }, - configurable: true - }) -}) - -function once (fn) { - var f = function () { - if (f.called) return f.value - f.called = true - return f.value = fn.apply(this, arguments) - } - f.called = false - return f -} - -function onceStrict (fn) { - var f = function () { - if (f.called) - throw new Error(f.onceError) - f.called = true - return f.value = fn.apply(this, arguments) - } - var name = fn.name || 'Function wrapped with `once`' - f.onceError = name + " shouldn't be called more than once" - f.called = false - return f -} - - -/***/ }), - -/***/ 4256: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -"use strict"; - - -var punycode = __nccwpck_require__(5477); -var mappingTable = __nccwpck_require__(2020); - -var PROCESSING_OPTIONS = { - TRANSITIONAL: 0, - NONTRANSITIONAL: 1 -}; - -function normalize(str) { // fix bug in v8 - return str.split('\u0000').map(function (s) { return s.normalize('NFC'); }).join('\u0000'); -} - -function findStatus(val) { - var start = 0; - var end = mappingTable.length - 1; - - while (start <= end) { - var mid = Math.floor((start + end) / 2); - - var target = mappingTable[mid]; - if (target[0][0] <= val && target[0][1] >= val) { - return target; - } else if (target[0][0] > val) { - end = mid - 1; - } else { - start = mid + 1; - } - } - - return null; -} - -var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; - -function countSymbols(string) { - return string - // replace every surrogate pair with a BMP symbol - .replace(regexAstralSymbols, '_') - // then get the length - .length; -} - -function mapChars(domain_name, useSTD3, processing_option) { - var hasError = false; - var processed = ""; - - var len = countSymbols(domain_name); - for (var i = 0; i < len; ++i) { - var codePoint = domain_name.codePointAt(i); - var status = findStatus(codePoint); - - switch (status[1]) { - case "disallowed": - hasError = true; - processed += String.fromCodePoint(codePoint); - break; - case "ignored": - break; - case "mapped": - processed += String.fromCodePoint.apply(String, status[2]); - break; - case "deviation": - if (processing_option === PROCESSING_OPTIONS.TRANSITIONAL) { - processed += String.fromCodePoint.apply(String, status[2]); - } else { - processed += String.fromCodePoint(codePoint); - } - break; - case "valid": - processed += String.fromCodePoint(codePoint); - break; - case "disallowed_STD3_mapped": - if (useSTD3) { - hasError = true; - processed += String.fromCodePoint(codePoint); - } else { - processed += String.fromCodePoint.apply(String, status[2]); - } - break; - case "disallowed_STD3_valid": - if (useSTD3) { - hasError = true; - } - - processed += String.fromCodePoint(codePoint); - break; - } - } - - return { - string: processed, - error: hasError - }; -} - -var combiningMarksRegex = /[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E4-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u19B0-\u19C0\u19C8\u19C9\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2D]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC7F-\uDC82\uDCB0-\uDCBA\uDD00-\uDD02\uDD27-\uDD34\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDE2C-\uDE37\uDEDF-\uDEEA\uDF01-\uDF03\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDCB0-\uDCC3\uDDAF-\uDDB5\uDDB8-\uDDC0\uDE30-\uDE40\uDEAB-\uDEB7]|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD83A[\uDCD0-\uDCD6]|\uDB40[\uDD00-\uDDEF]/; - -function validateLabel(label, processing_option) { - if (label.substr(0, 4) === "xn--") { - label = punycode.toUnicode(label); - processing_option = PROCESSING_OPTIONS.NONTRANSITIONAL; - } - - var error = false; - - if (normalize(label) !== label || - (label[3] === "-" && label[4] === "-") || - label[0] === "-" || label[label.length - 1] === "-" || - label.indexOf(".") !== -1 || - label.search(combiningMarksRegex) === 0) { - error = true; - } - - var len = countSymbols(label); - for (var i = 0; i < len; ++i) { - var status = findStatus(label.codePointAt(i)); - if ((processing === PROCESSING_OPTIONS.TRANSITIONAL && status[1] !== "valid") || - (processing === PROCESSING_OPTIONS.NONTRANSITIONAL && - status[1] !== "valid" && status[1] !== "deviation")) { - error = true; - break; - } - } - - return { - label: label, - error: error - }; -} - -function processing(domain_name, useSTD3, processing_option) { - var result = mapChars(domain_name, useSTD3, processing_option); - result.string = normalize(result.string); - - var labels = result.string.split("."); - for (var i = 0; i < labels.length; ++i) { - try { - var validation = validateLabel(labels[i]); - labels[i] = validation.label; - result.error = result.error || validation.error; - } catch(e) { - result.error = true; - } - } - - return { - string: labels.join("."), - error: result.error - }; -} - -module.exports.toASCII = function(domain_name, useSTD3, processing_option, verifyDnsLength) { - var result = processing(domain_name, useSTD3, processing_option); - var labels = result.string.split("."); - labels = labels.map(function(l) { - try { - return punycode.toASCII(l); - } catch(e) { - result.error = true; - return l; - } - }); - - if (verifyDnsLength) { - var total = labels.slice(0, labels.length - 1).join(".").length; - if (total.length > 253 || total.length === 0) { - result.error = true; - } - - for (var i=0; i < labels.length; ++i) { - if (labels.length > 63 || labels.length === 0) { - result.error = true; - break; - } - } - } - - if (result.error) return null; - return labels.join("."); -}; - -module.exports.toUnicode = function(domain_name, useSTD3) { - var result = processing(domain_name, useSTD3, PROCESSING_OPTIONS.NONTRANSITIONAL); - - return { - domain: result.string, - error: result.error - }; -}; - -module.exports.PROCESSING_OPTIONS = PROCESSING_OPTIONS; - - -/***/ }), - -/***/ 4294: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -module.exports = __nccwpck_require__(4219); - - -/***/ }), - -/***/ 4219: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -var net = __nccwpck_require__(1808); -var tls = __nccwpck_require__(4404); -var http = __nccwpck_require__(3685); -var https = __nccwpck_require__(5687); -var events = __nccwpck_require__(2361); -var assert = __nccwpck_require__(9491); -var util = __nccwpck_require__(3837); - - -exports.httpOverHttp = httpOverHttp; -exports.httpsOverHttp = httpsOverHttp; -exports.httpOverHttps = httpOverHttps; -exports.httpsOverHttps = httpsOverHttps; - - -function httpOverHttp(options) { - var agent = new TunnelingAgent(options); - agent.request = http.request; - return agent; -} - -function httpsOverHttp(options) { - var agent = new TunnelingAgent(options); - agent.request = http.request; - agent.createSocket = createSecureSocket; - agent.defaultPort = 443; - return agent; -} - -function httpOverHttps(options) { - var agent = new TunnelingAgent(options); - agent.request = https.request; - return agent; -} - -function httpsOverHttps(options) { - var agent = new TunnelingAgent(options); - agent.request = https.request; - agent.createSocket = createSecureSocket; - agent.defaultPort = 443; - return agent; -} - - -function TunnelingAgent(options) { - var self = this; - self.options = options || {}; - self.proxyOptions = self.options.proxy || {}; - self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets; - self.requests = []; - self.sockets = []; - - self.on('free', function onFree(socket, host, port, localAddress) { - var options = toOptions(host, port, localAddress); - for (var i = 0, len = self.requests.length; i < len; ++i) { - var pending = self.requests[i]; - if (pending.host === options.host && pending.port === options.port) { - // Detect the request to connect same origin server, - // reuse the connection. - self.requests.splice(i, 1); - pending.request.onSocket(socket); - return; - } - } - socket.destroy(); - self.removeSocket(socket); - }); -} -util.inherits(TunnelingAgent, events.EventEmitter); - -TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) { - var self = this; - var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress)); - - if (self.sockets.length >= this.maxSockets) { - // We are over limit so we'll add it to the queue. - self.requests.push(options); - return; - } - - // If we are under maxSockets create a new one. - self.createSocket(options, function(socket) { - socket.on('free', onFree); - socket.on('close', onCloseOrRemove); - socket.on('agentRemove', onCloseOrRemove); - req.onSocket(socket); - - function onFree() { - self.emit('free', socket, options); - } - - function onCloseOrRemove(err) { - self.removeSocket(socket); - socket.removeListener('free', onFree); - socket.removeListener('close', onCloseOrRemove); - socket.removeListener('agentRemove', onCloseOrRemove); - } - }); -}; - -TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { - var self = this; - var placeholder = {}; - self.sockets.push(placeholder); - - var connectOptions = mergeOptions({}, self.proxyOptions, { - method: 'CONNECT', - path: options.host + ':' + options.port, - agent: false, - headers: { - host: options.host + ':' + options.port - } - }); - if (options.localAddress) { - connectOptions.localAddress = options.localAddress; - } - if (connectOptions.proxyAuth) { - connectOptions.headers = connectOptions.headers || {}; - connectOptions.headers['Proxy-Authorization'] = 'Basic ' + - new Buffer(connectOptions.proxyAuth).toString('base64'); - } - - debug('making CONNECT request'); - var connectReq = self.request(connectOptions); - connectReq.useChunkedEncodingByDefault = false; // for v0.6 - connectReq.once('response', onResponse); // for v0.6 - connectReq.once('upgrade', onUpgrade); // for v0.6 - connectReq.once('connect', onConnect); // for v0.7 or later - connectReq.once('error', onError); - connectReq.end(); - - function onResponse(res) { - // Very hacky. This is necessary to avoid http-parser leaks. - res.upgrade = true; - } - - function onUpgrade(res, socket, head) { - // Hacky. - process.nextTick(function() { - onConnect(res, socket, head); - }); - } - - function onConnect(res, socket, head) { - connectReq.removeAllListeners(); - socket.removeAllListeners(); - - if (res.statusCode !== 200) { - debug('tunneling socket could not be established, statusCode=%d', - res.statusCode); - socket.destroy(); - var error = new Error('tunneling socket could not be established, ' + - 'statusCode=' + res.statusCode); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - return; - } - if (head.length > 0) { - debug('got illegal response body from proxy'); - socket.destroy(); - var error = new Error('got illegal response body from proxy'); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - return; - } - debug('tunneling connection has established'); - self.sockets[self.sockets.indexOf(placeholder)] = socket; - return cb(socket); - } - - function onError(cause) { - connectReq.removeAllListeners(); - - debug('tunneling socket could not be established, cause=%s\n', - cause.message, cause.stack); - var error = new Error('tunneling socket could not be established, ' + - 'cause=' + cause.message); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - } -}; - -TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { - var pos = this.sockets.indexOf(socket) - if (pos === -1) { - return; - } - this.sockets.splice(pos, 1); - - var pending = this.requests.shift(); - if (pending) { - // If we have pending requests and a socket gets closed a new one - // needs to be created to take over in the pool for the one that closed. - this.createSocket(pending, function(socket) { - pending.request.onSocket(socket); - }); - } -}; - -function createSecureSocket(options, cb) { - var self = this; - TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { - var hostHeader = options.request.getHeader('host'); - var tlsOptions = mergeOptions({}, self.options, { - socket: socket, - servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host - }); - - // 0 is dummy port for v0.6 - var secureSocket = tls.connect(0, tlsOptions); - self.sockets[self.sockets.indexOf(socket)] = secureSocket; - cb(secureSocket); - }); -} - - -function toOptions(host, port, localAddress) { - if (typeof host === 'string') { // since v0.10 - return { - host: host, - port: port, - localAddress: localAddress - }; - } - return host; // for v0.11 or later -} - -function mergeOptions(target) { - for (var i = 1, len = arguments.length; i < len; ++i) { - var overrides = arguments[i]; - if (typeof overrides === 'object') { - var keys = Object.keys(overrides); - for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { - var k = keys[j]; - if (overrides[k] !== undefined) { - target[k] = overrides[k]; - } - } - } - } - return target; -} - - -var debug; -if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { - debug = function() { - var args = Array.prototype.slice.call(arguments); - if (typeof args[0] === 'string') { - args[0] = 'TUNNEL: ' + args[0]; - } else { - args.unshift('TUNNEL:'); - } - console.error.apply(console, args); - } -} else { - debug = function() {}; -} -exports.debug = debug; // for test - - -/***/ }), - -/***/ 5030: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ value: true })); - -function getUserAgent() { - if (typeof navigator === "object" && "userAgent" in navigator) { - return navigator.userAgent; - } - - if (typeof process === "object" && "version" in process) { - return `Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`; - } - - return ""; -} - -exports.getUserAgent = getUserAgent; -//# sourceMappingURL=index.js.map - - -/***/ }), - -/***/ 5840: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -Object.defineProperty(exports, "v1", ({ - enumerable: true, - get: function () { - return _v.default; - } -})); -Object.defineProperty(exports, "v3", ({ - enumerable: true, - get: function () { - return _v2.default; - } -})); -Object.defineProperty(exports, "v4", ({ - enumerable: true, - get: function () { - return _v3.default; - } -})); -Object.defineProperty(exports, "v5", ({ - enumerable: true, - get: function () { - return _v4.default; - } -})); -Object.defineProperty(exports, "NIL", ({ - enumerable: true, - get: function () { - return _nil.default; - } -})); -Object.defineProperty(exports, "version", ({ - enumerable: true, - get: function () { - return _version.default; - } -})); -Object.defineProperty(exports, "validate", ({ - enumerable: true, - get: function () { - return _validate.default; - } -})); -Object.defineProperty(exports, "stringify", ({ - enumerable: true, - get: function () { - return _stringify.default; - } -})); -Object.defineProperty(exports, "parse", ({ - enumerable: true, - get: function () { - return _parse.default; - } -})); - -var _v = _interopRequireDefault(__nccwpck_require__(8628)); - -var _v2 = _interopRequireDefault(__nccwpck_require__(6409)); - -var _v3 = _interopRequireDefault(__nccwpck_require__(5122)); - -var _v4 = _interopRequireDefault(__nccwpck_require__(9120)); - -var _nil = _interopRequireDefault(__nccwpck_require__(5332)); - -var _version = _interopRequireDefault(__nccwpck_require__(1595)); - -var _validate = _interopRequireDefault(__nccwpck_require__(6900)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); - -var _parse = _interopRequireDefault(__nccwpck_require__(2746)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), - -/***/ 4569: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function md5(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('md5').update(bytes).digest(); -} - -var _default = md5; -exports["default"] = _default; - -/***/ }), - -/***/ 5332: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = '00000000-0000-0000-0000-000000000000'; -exports["default"] = _default; - -/***/ }), - -/***/ 2746: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(6900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function parse(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - let v; - const arr = new Uint8Array(16); // Parse ########-....-....-....-............ - - arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; - arr[1] = v >>> 16 & 0xff; - arr[2] = v >>> 8 & 0xff; - arr[3] = v & 0xff; // Parse ........-####-....-....-............ - - arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; - arr[5] = v & 0xff; // Parse ........-....-####-....-............ - - arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; - arr[7] = v & 0xff; // Parse ........-....-....-####-............ - - arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; - arr[9] = v & 0xff; // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - - arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; - arr[11] = v / 0x100000000 & 0xff; - arr[12] = v >>> 24 & 0xff; - arr[13] = v >>> 16 & 0xff; - arr[14] = v >>> 8 & 0xff; - arr[15] = v & 0xff; - return arr; -} - -var _default = parse; -exports["default"] = _default; - -/***/ }), - -/***/ 814: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; -exports["default"] = _default; - -/***/ }), - -/***/ 807: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = rng; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate - -let poolPtr = rnds8Pool.length; - -function rng() { - if (poolPtr > rnds8Pool.length - 16) { - _crypto.default.randomFillSync(rnds8Pool); - - poolPtr = 0; - } - - return rnds8Pool.slice(poolPtr, poolPtr += 16); -} - -/***/ }), - -/***/ 5274: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function sha1(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('sha1').update(bytes).digest(); -} - -var _default = sha1; -exports["default"] = _default; - -/***/ }), - -/***/ 8950: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(6900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -const byteToHex = []; - -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 0x100).toString(16).substr(1)); -} - -function stringify(arr, offset = 0) { - // Note: Be careful editing this code! It's been tuned for performance - // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 - const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one - // of the following: - // - One or more input array values don't map to a hex octet (leading to - // "undefined" in the uuid) - // - Invalid input values for the RFC `version` or `variant` fields - - if (!(0, _validate.default)(uuid)) { - throw TypeError('Stringified UUID is invalid'); - } - - return uuid; -} - -var _default = stringify; -exports["default"] = _default; - -/***/ }), - -/***/ 8628: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _rng = _interopRequireDefault(__nccwpck_require__(807)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// **`v1()` - Generate time-based UUID** -// -// Inspired by https://github.com/LiosK/UUID.js -// and http://docs.python.org/library/uuid.html -let _nodeId; - -let _clockseq; // Previous uuid creation time - - -let _lastMSecs = 0; -let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details - -function v1(options, buf, offset) { - let i = buf && offset || 0; - const b = buf || new Array(16); - options = options || {}; - let node = options.node || _nodeId; - let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not - // specified. We do this lazily to minimize issues related to insufficient - // system entropy. See #189 - - if (node == null || clockseq == null) { - const seedBytes = options.random || (options.rng || _rng.default)(); - - if (node == null) { - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) - node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; - } - - if (clockseq == null) { - // Per 4.2.2, randomize (14 bit) clockseq - clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; - } - } // UUID timestamps are 100 nano-second units since the Gregorian epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. - - - let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock - // cycle to simulate higher resolution clock - - let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) - - const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression - - if (dt < 0 && options.clockseq === undefined) { - clockseq = clockseq + 1 & 0x3fff; - } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new - // time interval - - - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } // Per 4.2.1.2 Throw error if too many uuids are requested - - - if (nsecs >= 10000) { - throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - - msecs += 12219292800000; // `time_low` - - const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; // `time_mid` - - const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; // `time_high_and_version` - - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - - b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - - b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` - - b[i++] = clockseq & 0xff; // `node` - - for (let n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf || (0, _stringify.default)(b); -} - -var _default = v1; -exports["default"] = _default; - -/***/ }), - -/***/ 6409: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(5998)); - -var _md = _interopRequireDefault(__nccwpck_require__(4569)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v3 = (0, _v.default)('v3', 0x30, _md.default); -var _default = v3; -exports["default"] = _default; - -/***/ }), - -/***/ 5998: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = _default; -exports.URL = exports.DNS = void 0; - -var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); - -var _parse = _interopRequireDefault(__nccwpck_require__(2746)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function stringToBytes(str) { - str = unescape(encodeURIComponent(str)); // UTF8 escape - - const bytes = []; - - for (let i = 0; i < str.length; ++i) { - bytes.push(str.charCodeAt(i)); - } - - return bytes; -} - -const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; -exports.DNS = DNS; -const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; -exports.URL = URL; - -function _default(name, version, hashfunc) { - function generateUUID(value, namespace, buf, offset) { - if (typeof value === 'string') { - value = stringToBytes(value); - } - - if (typeof namespace === 'string') { - namespace = (0, _parse.default)(namespace); - } - - if (namespace.length !== 16) { - throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); - } // Compute hash of namespace and value, Per 4.3 - // Future: Use spread syntax when supported on all platforms, e.g. `bytes = - // hashfunc([...namespace, ... value])` - - - let bytes = new Uint8Array(16 + value.length); - bytes.set(namespace); - bytes.set(value, namespace.length); - bytes = hashfunc(bytes); - bytes[6] = bytes[6] & 0x0f | version; - bytes[8] = bytes[8] & 0x3f | 0x80; - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = bytes[i]; - } - - return buf; - } - - return (0, _stringify.default)(bytes); - } // Function#name is not settable on some platforms (#270) - - - try { - generateUUID.name = name; // eslint-disable-next-line no-empty - } catch (err) {} // For CommonJS default export support - - - generateUUID.DNS = DNS; - generateUUID.URL = URL; - return generateUUID; -} - -/***/ }), - -/***/ 5122: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _rng = _interopRequireDefault(__nccwpck_require__(807)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function v4(options, buf, offset) { - options = options || {}; - - const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - - - rnds[6] = rnds[6] & 0x0f | 0x40; - rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = rnds[i]; - } - - return buf; - } - - return (0, _stringify.default)(rnds); -} - -var _default = v4; -exports["default"] = _default; - -/***/ }), - -/***/ 9120: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(5998)); - -var _sha = _interopRequireDefault(__nccwpck_require__(5274)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v5 = (0, _v.default)('v5', 0x50, _sha.default); -var _default = v5; -exports["default"] = _default; - -/***/ }), - -/***/ 6900: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _regex = _interopRequireDefault(__nccwpck_require__(814)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function validate(uuid) { - return typeof uuid === 'string' && _regex.default.test(uuid); -} - -var _default = validate; -exports["default"] = _default; - -/***/ }), - -/***/ 1595: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(6900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function version(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - return parseInt(uuid.substr(14, 1), 16); -} - -var _default = version; -exports["default"] = _default; - -/***/ }), - -/***/ 7537: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -const usm = __nccwpck_require__(2158); - -exports.implementation = class URLImpl { - constructor(constructorArgs) { - const url = constructorArgs[0]; - const base = constructorArgs[1]; - - let parsedBase = null; - if (base !== undefined) { - parsedBase = usm.basicURLParse(base); - if (parsedBase === "failure") { - throw new TypeError("Invalid base URL"); - } - } - - const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase }); - if (parsedURL === "failure") { - throw new TypeError("Invalid URL"); - } - - this._url = parsedURL; - - // TODO: query stuff - } - - get href() { - return usm.serializeURL(this._url); - } - - set href(v) { - const parsedURL = usm.basicURLParse(v); - if (parsedURL === "failure") { - throw new TypeError("Invalid URL"); - } - - this._url = parsedURL; - } - - get origin() { - return usm.serializeURLOrigin(this._url); - } - - get protocol() { - return this._url.scheme + ":"; - } - - set protocol(v) { - usm.basicURLParse(v + ":", { url: this._url, stateOverride: "scheme start" }); - } - - get username() { - return this._url.username; - } - - set username(v) { - if (usm.cannotHaveAUsernamePasswordPort(this._url)) { - return; - } - - usm.setTheUsername(this._url, v); - } - - get password() { - return this._url.password; - } - - set password(v) { - if (usm.cannotHaveAUsernamePasswordPort(this._url)) { - return; - } - - usm.setThePassword(this._url, v); - } - - get host() { - const url = this._url; - - if (url.host === null) { - return ""; - } - - if (url.port === null) { - return usm.serializeHost(url.host); - } - - return usm.serializeHost(url.host) + ":" + usm.serializeInteger(url.port); - } - - set host(v) { - if (this._url.cannotBeABaseURL) { - return; - } - - usm.basicURLParse(v, { url: this._url, stateOverride: "host" }); - } - - get hostname() { - if (this._url.host === null) { - return ""; - } - - return usm.serializeHost(this._url.host); - } - - set hostname(v) { - if (this._url.cannotBeABaseURL) { - return; - } - - usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" }); - } - - get port() { - if (this._url.port === null) { - return ""; - } - - return usm.serializeInteger(this._url.port); - } - - set port(v) { - if (usm.cannotHaveAUsernamePasswordPort(this._url)) { - return; - } - - if (v === "") { - this._url.port = null; - } else { - usm.basicURLParse(v, { url: this._url, stateOverride: "port" }); - } - } - - get pathname() { - if (this._url.cannotBeABaseURL) { - return this._url.path[0]; - } - - if (this._url.path.length === 0) { - return ""; - } - - return "/" + this._url.path.join("/"); - } - - set pathname(v) { - if (this._url.cannotBeABaseURL) { - return; - } - - this._url.path = []; - usm.basicURLParse(v, { url: this._url, stateOverride: "path start" }); - } - - get search() { - if (this._url.query === null || this._url.query === "") { - return ""; - } - - return "?" + this._url.query; - } - - set search(v) { - // TODO: query stuff - - const url = this._url; - - if (v === "") { - url.query = null; - return; - } - - const input = v[0] === "?" ? v.substring(1) : v; - url.query = ""; - usm.basicURLParse(input, { url, stateOverride: "query" }); - } - - get hash() { - if (this._url.fragment === null || this._url.fragment === "") { - return ""; - } - - return "#" + this._url.fragment; - } - - set hash(v) { - if (v === "") { - this._url.fragment = null; - return; - } - - const input = v[0] === "#" ? v.substring(1) : v; - this._url.fragment = ""; - usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" }); - } - - toJSON() { - return this.href; - } -}; - - -/***/ }), - -/***/ 3394: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -"use strict"; - - -const conversions = __nccwpck_require__(6059); -const utils = __nccwpck_require__(3185); -const Impl = __nccwpck_require__(7537); - -const impl = utils.implSymbol; - -function URL(url) { - if (!this || this[impl] || !(this instanceof URL)) { - throw new TypeError("Failed to construct 'URL': Please use the 'new' operator, this DOM object constructor cannot be called as a function."); - } - if (arguments.length < 1) { - throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present."); - } - const args = []; - for (let i = 0; i < arguments.length && i < 2; ++i) { - args[i] = arguments[i]; - } - args[0] = conversions["USVString"](args[0]); - if (args[1] !== undefined) { - args[1] = conversions["USVString"](args[1]); - } - - module.exports.setup(this, args); -} - -URL.prototype.toJSON = function toJSON() { - if (!this || !module.exports.is(this)) { - throw new TypeError("Illegal invocation"); - } - const args = []; - for (let i = 0; i < arguments.length && i < 0; ++i) { - args[i] = arguments[i]; - } - return this[impl].toJSON.apply(this[impl], args); -}; -Object.defineProperty(URL.prototype, "href", { - get() { - return this[impl].href; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].href = V; - }, - enumerable: true, - configurable: true -}); - -URL.prototype.toString = function () { - if (!this || !module.exports.is(this)) { - throw new TypeError("Illegal invocation"); - } - return this.href; -}; - -Object.defineProperty(URL.prototype, "origin", { - get() { - return this[impl].origin; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "protocol", { - get() { - return this[impl].protocol; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].protocol = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "username", { - get() { - return this[impl].username; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].username = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "password", { - get() { - return this[impl].password; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].password = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "host", { - get() { - return this[impl].host; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].host = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "hostname", { - get() { - return this[impl].hostname; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].hostname = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "port", { - get() { - return this[impl].port; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].port = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "pathname", { - get() { - return this[impl].pathname; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].pathname = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "search", { - get() { - return this[impl].search; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].search = V; - }, - enumerable: true, - configurable: true -}); - -Object.defineProperty(URL.prototype, "hash", { - get() { - return this[impl].hash; - }, - set(V) { - V = conversions["USVString"](V); - this[impl].hash = V; - }, - enumerable: true, - configurable: true -}); - - -module.exports = { - is(obj) { - return !!obj && obj[impl] instanceof Impl.implementation; - }, - create(constructorArgs, privateData) { - let obj = Object.create(URL.prototype); - this.setup(obj, constructorArgs, privateData); - return obj; - }, - setup(obj, constructorArgs, privateData) { - if (!privateData) privateData = {}; - privateData.wrapper = obj; - - obj[impl] = new Impl.implementation(constructorArgs, privateData); - obj[impl][utils.wrapperSymbol] = obj; - }, - interface: URL, - expose: { - Window: { URL: URL }, - Worker: { URL: URL } - } -}; - - - -/***/ }), - -/***/ 8665: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -exports.URL = __nccwpck_require__(3394)["interface"]; -exports.serializeURL = __nccwpck_require__(2158).serializeURL; -exports.serializeURLOrigin = __nccwpck_require__(2158).serializeURLOrigin; -exports.basicURLParse = __nccwpck_require__(2158).basicURLParse; -exports.setTheUsername = __nccwpck_require__(2158).setTheUsername; -exports.setThePassword = __nccwpck_require__(2158).setThePassword; -exports.serializeHost = __nccwpck_require__(2158).serializeHost; -exports.serializeInteger = __nccwpck_require__(2158).serializeInteger; -exports.parseURL = __nccwpck_require__(2158).parseURL; - - -/***/ }), - -/***/ 2158: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -"use strict"; - -const punycode = __nccwpck_require__(5477); -const tr46 = __nccwpck_require__(4256); - -const specialSchemes = { - ftp: 21, - file: null, - gopher: 70, - http: 80, - https: 443, - ws: 80, - wss: 443 -}; - -const failure = Symbol("failure"); - -function countSymbols(str) { - return punycode.ucs2.decode(str).length; -} - -function at(input, idx) { - const c = input[idx]; - return isNaN(c) ? undefined : String.fromCodePoint(c); -} - -function isASCIIDigit(c) { - return c >= 0x30 && c <= 0x39; -} - -function isASCIIAlpha(c) { - return (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A); -} - -function isASCIIAlphanumeric(c) { - return isASCIIAlpha(c) || isASCIIDigit(c); -} - -function isASCIIHex(c) { - return isASCIIDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66); -} - -function isSingleDot(buffer) { - return buffer === "." || buffer.toLowerCase() === "%2e"; -} - -function isDoubleDot(buffer) { - buffer = buffer.toLowerCase(); - return buffer === ".." || buffer === "%2e." || buffer === ".%2e" || buffer === "%2e%2e"; -} - -function isWindowsDriveLetterCodePoints(cp1, cp2) { - return isASCIIAlpha(cp1) && (cp2 === 58 || cp2 === 124); -} - -function isWindowsDriveLetterString(string) { - return string.length === 2 && isASCIIAlpha(string.codePointAt(0)) && (string[1] === ":" || string[1] === "|"); -} - -function isNormalizedWindowsDriveLetterString(string) { - return string.length === 2 && isASCIIAlpha(string.codePointAt(0)) && string[1] === ":"; -} - -function containsForbiddenHostCodePoint(string) { - return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|%|\/|:|\?|@|\[|\\|\]/) !== -1; -} - -function containsForbiddenHostCodePointExcludingPercent(string) { - return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|\/|:|\?|@|\[|\\|\]/) !== -1; -} - -function isSpecialScheme(scheme) { - return specialSchemes[scheme] !== undefined; -} - -function isSpecial(url) { - return isSpecialScheme(url.scheme); -} - -function defaultPort(scheme) { - return specialSchemes[scheme]; -} - -function percentEncode(c) { - let hex = c.toString(16).toUpperCase(); - if (hex.length === 1) { - hex = "0" + hex; - } - - return "%" + hex; -} - -function utf8PercentEncode(c) { - const buf = new Buffer(c); - - let str = ""; - - for (let i = 0; i < buf.length; ++i) { - str += percentEncode(buf[i]); - } - - return str; -} - -function utf8PercentDecode(str) { - const input = new Buffer(str); - const output = []; - for (let i = 0; i < input.length; ++i) { - if (input[i] !== 37) { - output.push(input[i]); - } else if (input[i] === 37 && isASCIIHex(input[i + 1]) && isASCIIHex(input[i + 2])) { - output.push(parseInt(input.slice(i + 1, i + 3).toString(), 16)); - i += 2; - } else { - output.push(input[i]); - } - } - return new Buffer(output).toString(); -} - -function isC0ControlPercentEncode(c) { - return c <= 0x1F || c > 0x7E; -} - -const extraPathPercentEncodeSet = new Set([32, 34, 35, 60, 62, 63, 96, 123, 125]); -function isPathPercentEncode(c) { - return isC0ControlPercentEncode(c) || extraPathPercentEncodeSet.has(c); -} - -const extraUserinfoPercentEncodeSet = - new Set([47, 58, 59, 61, 64, 91, 92, 93, 94, 124]); -function isUserinfoPercentEncode(c) { - return isPathPercentEncode(c) || extraUserinfoPercentEncodeSet.has(c); -} - -function percentEncodeChar(c, encodeSetPredicate) { - const cStr = String.fromCodePoint(c); - - if (encodeSetPredicate(c)) { - return utf8PercentEncode(cStr); - } - - return cStr; -} - -function parseIPv4Number(input) { - let R = 10; - - if (input.length >= 2 && input.charAt(0) === "0" && input.charAt(1).toLowerCase() === "x") { - input = input.substring(2); - R = 16; - } else if (input.length >= 2 && input.charAt(0) === "0") { - input = input.substring(1); - R = 8; - } - - if (input === "") { - return 0; - } - - const regex = R === 10 ? /[^0-9]/ : (R === 16 ? /[^0-9A-Fa-f]/ : /[^0-7]/); - if (regex.test(input)) { - return failure; - } - - return parseInt(input, R); -} - -function parseIPv4(input) { - const parts = input.split("."); - if (parts[parts.length - 1] === "") { - if (parts.length > 1) { - parts.pop(); - } - } - - if (parts.length > 4) { - return input; - } - - const numbers = []; - for (const part of parts) { - if (part === "") { - return input; - } - const n = parseIPv4Number(part); - if (n === failure) { - return input; - } - - numbers.push(n); - } - - for (let i = 0; i < numbers.length - 1; ++i) { - if (numbers[i] > 255) { - return failure; - } - } - if (numbers[numbers.length - 1] >= Math.pow(256, 5 - numbers.length)) { - return failure; - } - - let ipv4 = numbers.pop(); - let counter = 0; - - for (const n of numbers) { - ipv4 += n * Math.pow(256, 3 - counter); - ++counter; - } - - return ipv4; -} - -function serializeIPv4(address) { - let output = ""; - let n = address; - - for (let i = 1; i <= 4; ++i) { - output = String(n % 256) + output; - if (i !== 4) { - output = "." + output; - } - n = Math.floor(n / 256); - } - - return output; -} - -function parseIPv6(input) { - const address = [0, 0, 0, 0, 0, 0, 0, 0]; - let pieceIndex = 0; - let compress = null; - let pointer = 0; - - input = punycode.ucs2.decode(input); - - if (input[pointer] === 58) { - if (input[pointer + 1] !== 58) { - return failure; - } - - pointer += 2; - ++pieceIndex; - compress = pieceIndex; - } - - while (pointer < input.length) { - if (pieceIndex === 8) { - return failure; - } - - if (input[pointer] === 58) { - if (compress !== null) { - return failure; - } - ++pointer; - ++pieceIndex; - compress = pieceIndex; - continue; - } - - let value = 0; - let length = 0; - - while (length < 4 && isASCIIHex(input[pointer])) { - value = value * 0x10 + parseInt(at(input, pointer), 16); - ++pointer; - ++length; - } - - if (input[pointer] === 46) { - if (length === 0) { - return failure; - } - - pointer -= length; - - if (pieceIndex > 6) { - return failure; - } - - let numbersSeen = 0; - - while (input[pointer] !== undefined) { - let ipv4Piece = null; - - if (numbersSeen > 0) { - if (input[pointer] === 46 && numbersSeen < 4) { - ++pointer; - } else { - return failure; - } - } - - if (!isASCIIDigit(input[pointer])) { - return failure; - } - - while (isASCIIDigit(input[pointer])) { - const number = parseInt(at(input, pointer)); - if (ipv4Piece === null) { - ipv4Piece = number; - } else if (ipv4Piece === 0) { - return failure; - } else { - ipv4Piece = ipv4Piece * 10 + number; - } - if (ipv4Piece > 255) { - return failure; - } - ++pointer; - } - - address[pieceIndex] = address[pieceIndex] * 0x100 + ipv4Piece; - - ++numbersSeen; - - if (numbersSeen === 2 || numbersSeen === 4) { - ++pieceIndex; - } - } - - if (numbersSeen !== 4) { - return failure; - } - - break; - } else if (input[pointer] === 58) { - ++pointer; - if (input[pointer] === undefined) { - return failure; - } - } else if (input[pointer] !== undefined) { - return failure; - } - - address[pieceIndex] = value; - ++pieceIndex; - } - - if (compress !== null) { - let swaps = pieceIndex - compress; - pieceIndex = 7; - while (pieceIndex !== 0 && swaps > 0) { - const temp = address[compress + swaps - 1]; - address[compress + swaps - 1] = address[pieceIndex]; - address[pieceIndex] = temp; - --pieceIndex; - --swaps; - } - } else if (compress === null && pieceIndex !== 8) { - return failure; - } - - return address; -} - -function serializeIPv6(address) { - let output = ""; - const seqResult = findLongestZeroSequence(address); - const compress = seqResult.idx; - let ignore0 = false; - - for (let pieceIndex = 0; pieceIndex <= 7; ++pieceIndex) { - if (ignore0 && address[pieceIndex] === 0) { - continue; - } else if (ignore0) { - ignore0 = false; - } - - if (compress === pieceIndex) { - const separator = pieceIndex === 0 ? "::" : ":"; - output += separator; - ignore0 = true; - continue; - } - - output += address[pieceIndex].toString(16); - - if (pieceIndex !== 7) { - output += ":"; - } - } - - return output; -} - -function parseHost(input, isSpecialArg) { - if (input[0] === "[") { - if (input[input.length - 1] !== "]") { - return failure; - } - - return parseIPv6(input.substring(1, input.length - 1)); - } - - if (!isSpecialArg) { - return parseOpaqueHost(input); - } - - const domain = utf8PercentDecode(input); - const asciiDomain = tr46.toASCII(domain, false, tr46.PROCESSING_OPTIONS.NONTRANSITIONAL, false); - if (asciiDomain === null) { - return failure; - } - - if (containsForbiddenHostCodePoint(asciiDomain)) { - return failure; - } - - const ipv4Host = parseIPv4(asciiDomain); - if (typeof ipv4Host === "number" || ipv4Host === failure) { - return ipv4Host; - } - - return asciiDomain; -} - -function parseOpaqueHost(input) { - if (containsForbiddenHostCodePointExcludingPercent(input)) { - return failure; - } - - let output = ""; - const decoded = punycode.ucs2.decode(input); - for (let i = 0; i < decoded.length; ++i) { - output += percentEncodeChar(decoded[i], isC0ControlPercentEncode); - } - return output; -} - -function findLongestZeroSequence(arr) { - let maxIdx = null; - let maxLen = 1; // only find elements > 1 - let currStart = null; - let currLen = 0; - - for (let i = 0; i < arr.length; ++i) { - if (arr[i] !== 0) { - if (currLen > maxLen) { - maxIdx = currStart; - maxLen = currLen; - } - - currStart = null; - currLen = 0; - } else { - if (currStart === null) { - currStart = i; - } - ++currLen; - } - } - - // if trailing zeros - if (currLen > maxLen) { - maxIdx = currStart; - maxLen = currLen; - } - - return { - idx: maxIdx, - len: maxLen - }; -} - -function serializeHost(host) { - if (typeof host === "number") { - return serializeIPv4(host); - } - - // IPv6 serializer - if (host instanceof Array) { - return "[" + serializeIPv6(host) + "]"; - } - - return host; -} - -function trimControlChars(url) { - return url.replace(/^[\u0000-\u001F\u0020]+|[\u0000-\u001F\u0020]+$/g, ""); -} - -function trimTabAndNewline(url) { - return url.replace(/\u0009|\u000A|\u000D/g, ""); -} - -function shortenPath(url) { - const path = url.path; - if (path.length === 0) { - return; - } - if (url.scheme === "file" && path.length === 1 && isNormalizedWindowsDriveLetter(path[0])) { - return; - } - - path.pop(); -} - -function includesCredentials(url) { - return url.username !== "" || url.password !== ""; -} - -function cannotHaveAUsernamePasswordPort(url) { - return url.host === null || url.host === "" || url.cannotBeABaseURL || url.scheme === "file"; -} - -function isNormalizedWindowsDriveLetter(string) { - return /^[A-Za-z]:$/.test(string); -} - -function URLStateMachine(input, base, encodingOverride, url, stateOverride) { - this.pointer = 0; - this.input = input; - this.base = base || null; - this.encodingOverride = encodingOverride || "utf-8"; - this.stateOverride = stateOverride; - this.url = url; - this.failure = false; - this.parseError = false; - - if (!this.url) { - this.url = { - scheme: "", - username: "", - password: "", - host: null, - port: null, - path: [], - query: null, - fragment: null, - - cannotBeABaseURL: false - }; - - const res = trimControlChars(this.input); - if (res !== this.input) { - this.parseError = true; - } - this.input = res; - } - - const res = trimTabAndNewline(this.input); - if (res !== this.input) { - this.parseError = true; - } - this.input = res; - - this.state = stateOverride || "scheme start"; - - this.buffer = ""; - this.atFlag = false; - this.arrFlag = false; - this.passwordTokenSeenFlag = false; - - this.input = punycode.ucs2.decode(this.input); - - for (; this.pointer <= this.input.length; ++this.pointer) { - const c = this.input[this.pointer]; - const cStr = isNaN(c) ? undefined : String.fromCodePoint(c); - - // exec state machine - const ret = this["parse " + this.state](c, cStr); - if (!ret) { - break; // terminate algorithm - } else if (ret === failure) { - this.failure = true; - break; - } - } -} - -URLStateMachine.prototype["parse scheme start"] = function parseSchemeStart(c, cStr) { - if (isASCIIAlpha(c)) { - this.buffer += cStr.toLowerCase(); - this.state = "scheme"; - } else if (!this.stateOverride) { - this.state = "no scheme"; - --this.pointer; - } else { - this.parseError = true; - return failure; - } - - return true; -}; - -URLStateMachine.prototype["parse scheme"] = function parseScheme(c, cStr) { - if (isASCIIAlphanumeric(c) || c === 43 || c === 45 || c === 46) { - this.buffer += cStr.toLowerCase(); - } else if (c === 58) { - if (this.stateOverride) { - if (isSpecial(this.url) && !isSpecialScheme(this.buffer)) { - return false; - } - - if (!isSpecial(this.url) && isSpecialScheme(this.buffer)) { - return false; - } - - if ((includesCredentials(this.url) || this.url.port !== null) && this.buffer === "file") { - return false; - } - - if (this.url.scheme === "file" && (this.url.host === "" || this.url.host === null)) { - return false; - } - } - this.url.scheme = this.buffer; - this.buffer = ""; - if (this.stateOverride) { - return false; - } - if (this.url.scheme === "file") { - if (this.input[this.pointer + 1] !== 47 || this.input[this.pointer + 2] !== 47) { - this.parseError = true; - } - this.state = "file"; - } else if (isSpecial(this.url) && this.base !== null && this.base.scheme === this.url.scheme) { - this.state = "special relative or authority"; - } else if (isSpecial(this.url)) { - this.state = "special authority slashes"; - } else if (this.input[this.pointer + 1] === 47) { - this.state = "path or authority"; - ++this.pointer; - } else { - this.url.cannotBeABaseURL = true; - this.url.path.push(""); - this.state = "cannot-be-a-base-URL path"; - } - } else if (!this.stateOverride) { - this.buffer = ""; - this.state = "no scheme"; - this.pointer = -1; - } else { - this.parseError = true; - return failure; - } - - return true; -}; - -URLStateMachine.prototype["parse no scheme"] = function parseNoScheme(c) { - if (this.base === null || (this.base.cannotBeABaseURL && c !== 35)) { - return failure; - } else if (this.base.cannotBeABaseURL && c === 35) { - this.url.scheme = this.base.scheme; - this.url.path = this.base.path.slice(); - this.url.query = this.base.query; - this.url.fragment = ""; - this.url.cannotBeABaseURL = true; - this.state = "fragment"; - } else if (this.base.scheme === "file") { - this.state = "file"; - --this.pointer; - } else { - this.state = "relative"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse special relative or authority"] = function parseSpecialRelativeOrAuthority(c) { - if (c === 47 && this.input[this.pointer + 1] === 47) { - this.state = "special authority ignore slashes"; - ++this.pointer; - } else { - this.parseError = true; - this.state = "relative"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse path or authority"] = function parsePathOrAuthority(c) { - if (c === 47) { - this.state = "authority"; - } else { - this.state = "path"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse relative"] = function parseRelative(c) { - this.url.scheme = this.base.scheme; - if (isNaN(c)) { - this.url.username = this.base.username; - this.url.password = this.base.password; - this.url.host = this.base.host; - this.url.port = this.base.port; - this.url.path = this.base.path.slice(); - this.url.query = this.base.query; - } else if (c === 47) { - this.state = "relative slash"; - } else if (c === 63) { - this.url.username = this.base.username; - this.url.password = this.base.password; - this.url.host = this.base.host; - this.url.port = this.base.port; - this.url.path = this.base.path.slice(); - this.url.query = ""; - this.state = "query"; - } else if (c === 35) { - this.url.username = this.base.username; - this.url.password = this.base.password; - this.url.host = this.base.host; - this.url.port = this.base.port; - this.url.path = this.base.path.slice(); - this.url.query = this.base.query; - this.url.fragment = ""; - this.state = "fragment"; - } else if (isSpecial(this.url) && c === 92) { - this.parseError = true; - this.state = "relative slash"; - } else { - this.url.username = this.base.username; - this.url.password = this.base.password; - this.url.host = this.base.host; - this.url.port = this.base.port; - this.url.path = this.base.path.slice(0, this.base.path.length - 1); - - this.state = "path"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse relative slash"] = function parseRelativeSlash(c) { - if (isSpecial(this.url) && (c === 47 || c === 92)) { - if (c === 92) { - this.parseError = true; - } - this.state = "special authority ignore slashes"; - } else if (c === 47) { - this.state = "authority"; - } else { - this.url.username = this.base.username; - this.url.password = this.base.password; - this.url.host = this.base.host; - this.url.port = this.base.port; - this.state = "path"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse special authority slashes"] = function parseSpecialAuthoritySlashes(c) { - if (c === 47 && this.input[this.pointer + 1] === 47) { - this.state = "special authority ignore slashes"; - ++this.pointer; - } else { - this.parseError = true; - this.state = "special authority ignore slashes"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse special authority ignore slashes"] = function parseSpecialAuthorityIgnoreSlashes(c) { - if (c !== 47 && c !== 92) { - this.state = "authority"; - --this.pointer; - } else { - this.parseError = true; - } - - return true; -}; - -URLStateMachine.prototype["parse authority"] = function parseAuthority(c, cStr) { - if (c === 64) { - this.parseError = true; - if (this.atFlag) { - this.buffer = "%40" + this.buffer; - } - this.atFlag = true; - - // careful, this is based on buffer and has its own pointer (this.pointer != pointer) and inner chars - const len = countSymbols(this.buffer); - for (let pointer = 0; pointer < len; ++pointer) { - const codePoint = this.buffer.codePointAt(pointer); - - if (codePoint === 58 && !this.passwordTokenSeenFlag) { - this.passwordTokenSeenFlag = true; - continue; - } - const encodedCodePoints = percentEncodeChar(codePoint, isUserinfoPercentEncode); - if (this.passwordTokenSeenFlag) { - this.url.password += encodedCodePoints; - } else { - this.url.username += encodedCodePoints; - } - } - this.buffer = ""; - } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || - (isSpecial(this.url) && c === 92)) { - if (this.atFlag && this.buffer === "") { - this.parseError = true; - return failure; - } - this.pointer -= countSymbols(this.buffer) + 1; - this.buffer = ""; - this.state = "host"; - } else { - this.buffer += cStr; - } - - return true; -}; - -URLStateMachine.prototype["parse hostname"] = -URLStateMachine.prototype["parse host"] = function parseHostName(c, cStr) { - if (this.stateOverride && this.url.scheme === "file") { - --this.pointer; - this.state = "file host"; - } else if (c === 58 && !this.arrFlag) { - if (this.buffer === "") { - this.parseError = true; - return failure; - } - - const host = parseHost(this.buffer, isSpecial(this.url)); - if (host === failure) { - return failure; - } - - this.url.host = host; - this.buffer = ""; - this.state = "port"; - if (this.stateOverride === "hostname") { - return false; - } - } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || - (isSpecial(this.url) && c === 92)) { - --this.pointer; - if (isSpecial(this.url) && this.buffer === "") { - this.parseError = true; - return failure; - } else if (this.stateOverride && this.buffer === "" && - (includesCredentials(this.url) || this.url.port !== null)) { - this.parseError = true; - return false; - } - - const host = parseHost(this.buffer, isSpecial(this.url)); - if (host === failure) { - return failure; - } - - this.url.host = host; - this.buffer = ""; - this.state = "path start"; - if (this.stateOverride) { - return false; - } - } else { - if (c === 91) { - this.arrFlag = true; - } else if (c === 93) { - this.arrFlag = false; - } - this.buffer += cStr; - } - - return true; -}; - -URLStateMachine.prototype["parse port"] = function parsePort(c, cStr) { - if (isASCIIDigit(c)) { - this.buffer += cStr; - } else if (isNaN(c) || c === 47 || c === 63 || c === 35 || - (isSpecial(this.url) && c === 92) || - this.stateOverride) { - if (this.buffer !== "") { - const port = parseInt(this.buffer); - if (port > Math.pow(2, 16) - 1) { - this.parseError = true; - return failure; - } - this.url.port = port === defaultPort(this.url.scheme) ? null : port; - this.buffer = ""; - } - if (this.stateOverride) { - return false; - } - this.state = "path start"; - --this.pointer; - } else { - this.parseError = true; - return failure; - } - - return true; -}; - -const fileOtherwiseCodePoints = new Set([47, 92, 63, 35]); - -URLStateMachine.prototype["parse file"] = function parseFile(c) { - this.url.scheme = "file"; - - if (c === 47 || c === 92) { - if (c === 92) { - this.parseError = true; - } - this.state = "file slash"; - } else if (this.base !== null && this.base.scheme === "file") { - if (isNaN(c)) { - this.url.host = this.base.host; - this.url.path = this.base.path.slice(); - this.url.query = this.base.query; - } else if (c === 63) { - this.url.host = this.base.host; - this.url.path = this.base.path.slice(); - this.url.query = ""; - this.state = "query"; - } else if (c === 35) { - this.url.host = this.base.host; - this.url.path = this.base.path.slice(); - this.url.query = this.base.query; - this.url.fragment = ""; - this.state = "fragment"; - } else { - if (this.input.length - this.pointer - 1 === 0 || // remaining consists of 0 code points - !isWindowsDriveLetterCodePoints(c, this.input[this.pointer + 1]) || - (this.input.length - this.pointer - 1 >= 2 && // remaining has at least 2 code points - !fileOtherwiseCodePoints.has(this.input[this.pointer + 2]))) { - this.url.host = this.base.host; - this.url.path = this.base.path.slice(); - shortenPath(this.url); - } else { - this.parseError = true; - } - - this.state = "path"; - --this.pointer; - } - } else { - this.state = "path"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse file slash"] = function parseFileSlash(c) { - if (c === 47 || c === 92) { - if (c === 92) { - this.parseError = true; - } - this.state = "file host"; - } else { - if (this.base !== null && this.base.scheme === "file") { - if (isNormalizedWindowsDriveLetterString(this.base.path[0])) { - this.url.path.push(this.base.path[0]); - } else { - this.url.host = this.base.host; - } - } - this.state = "path"; - --this.pointer; - } - - return true; -}; - -URLStateMachine.prototype["parse file host"] = function parseFileHost(c, cStr) { - if (isNaN(c) || c === 47 || c === 92 || c === 63 || c === 35) { - --this.pointer; - if (!this.stateOverride && isWindowsDriveLetterString(this.buffer)) { - this.parseError = true; - this.state = "path"; - } else if (this.buffer === "") { - this.url.host = ""; - if (this.stateOverride) { - return false; - } - this.state = "path start"; - } else { - let host = parseHost(this.buffer, isSpecial(this.url)); - if (host === failure) { - return failure; - } - if (host === "localhost") { - host = ""; - } - this.url.host = host; - - if (this.stateOverride) { - return false; - } - - this.buffer = ""; - this.state = "path start"; - } - } else { - this.buffer += cStr; - } - - return true; -}; - -URLStateMachine.prototype["parse path start"] = function parsePathStart(c) { - if (isSpecial(this.url)) { - if (c === 92) { - this.parseError = true; - } - this.state = "path"; - - if (c !== 47 && c !== 92) { - --this.pointer; - } - } else if (!this.stateOverride && c === 63) { - this.url.query = ""; - this.state = "query"; - } else if (!this.stateOverride && c === 35) { - this.url.fragment = ""; - this.state = "fragment"; - } else if (c !== undefined) { - this.state = "path"; - if (c !== 47) { - --this.pointer; - } - } - - return true; -}; - -URLStateMachine.prototype["parse path"] = function parsePath(c) { - if (isNaN(c) || c === 47 || (isSpecial(this.url) && c === 92) || - (!this.stateOverride && (c === 63 || c === 35))) { - if (isSpecial(this.url) && c === 92) { - this.parseError = true; - } - - if (isDoubleDot(this.buffer)) { - shortenPath(this.url); - if (c !== 47 && !(isSpecial(this.url) && c === 92)) { - this.url.path.push(""); - } - } else if (isSingleDot(this.buffer) && c !== 47 && - !(isSpecial(this.url) && c === 92)) { - this.url.path.push(""); - } else if (!isSingleDot(this.buffer)) { - if (this.url.scheme === "file" && this.url.path.length === 0 && isWindowsDriveLetterString(this.buffer)) { - if (this.url.host !== "" && this.url.host !== null) { - this.parseError = true; - this.url.host = ""; - } - this.buffer = this.buffer[0] + ":"; - } - this.url.path.push(this.buffer); - } - this.buffer = ""; - if (this.url.scheme === "file" && (c === undefined || c === 63 || c === 35)) { - while (this.url.path.length > 1 && this.url.path[0] === "") { - this.parseError = true; - this.url.path.shift(); - } - } - if (c === 63) { - this.url.query = ""; - this.state = "query"; - } - if (c === 35) { - this.url.fragment = ""; - this.state = "fragment"; - } - } else { - // TODO: If c is not a URL code point and not "%", parse error. - - if (c === 37 && - (!isASCIIHex(this.input[this.pointer + 1]) || - !isASCIIHex(this.input[this.pointer + 2]))) { - this.parseError = true; - } - - this.buffer += percentEncodeChar(c, isPathPercentEncode); - } - - return true; -}; - -URLStateMachine.prototype["parse cannot-be-a-base-URL path"] = function parseCannotBeABaseURLPath(c) { - if (c === 63) { - this.url.query = ""; - this.state = "query"; - } else if (c === 35) { - this.url.fragment = ""; - this.state = "fragment"; - } else { - // TODO: Add: not a URL code point - if (!isNaN(c) && c !== 37) { - this.parseError = true; - } - - if (c === 37 && - (!isASCIIHex(this.input[this.pointer + 1]) || - !isASCIIHex(this.input[this.pointer + 2]))) { - this.parseError = true; - } - - if (!isNaN(c)) { - this.url.path[0] = this.url.path[0] + percentEncodeChar(c, isC0ControlPercentEncode); - } - } - - return true; -}; - -URLStateMachine.prototype["parse query"] = function parseQuery(c, cStr) { - if (isNaN(c) || (!this.stateOverride && c === 35)) { - if (!isSpecial(this.url) || this.url.scheme === "ws" || this.url.scheme === "wss") { - this.encodingOverride = "utf-8"; - } - - const buffer = new Buffer(this.buffer); // TODO: Use encoding override instead - for (let i = 0; i < buffer.length; ++i) { - if (buffer[i] < 0x21 || buffer[i] > 0x7E || buffer[i] === 0x22 || buffer[i] === 0x23 || - buffer[i] === 0x3C || buffer[i] === 0x3E) { - this.url.query += percentEncode(buffer[i]); - } else { - this.url.query += String.fromCodePoint(buffer[i]); - } - } - - this.buffer = ""; - if (c === 35) { - this.url.fragment = ""; - this.state = "fragment"; - } - } else { - // TODO: If c is not a URL code point and not "%", parse error. - if (c === 37 && - (!isASCIIHex(this.input[this.pointer + 1]) || - !isASCIIHex(this.input[this.pointer + 2]))) { - this.parseError = true; - } - - this.buffer += cStr; - } - - return true; -}; - -URLStateMachine.prototype["parse fragment"] = function parseFragment(c) { - if (isNaN(c)) { // do nothing - } else if (c === 0x0) { - this.parseError = true; - } else { - // TODO: If c is not a URL code point and not "%", parse error. - if (c === 37 && - (!isASCIIHex(this.input[this.pointer + 1]) || - !isASCIIHex(this.input[this.pointer + 2]))) { - this.parseError = true; - } - - this.url.fragment += percentEncodeChar(c, isC0ControlPercentEncode); - } - - return true; -}; - -function serializeURL(url, excludeFragment) { - let output = url.scheme + ":"; - if (url.host !== null) { - output += "//"; - - if (url.username !== "" || url.password !== "") { - output += url.username; - if (url.password !== "") { - output += ":" + url.password; - } - output += "@"; - } - - output += serializeHost(url.host); - - if (url.port !== null) { - output += ":" + url.port; - } - } else if (url.host === null && url.scheme === "file") { - output += "//"; - } - - if (url.cannotBeABaseURL) { - output += url.path[0]; - } else { - for (const string of url.path) { - output += "/" + string; - } - } - - if (url.query !== null) { - output += "?" + url.query; - } - - if (!excludeFragment && url.fragment !== null) { - output += "#" + url.fragment; - } - - return output; -} - -function serializeOrigin(tuple) { - let result = tuple.scheme + "://"; - result += serializeHost(tuple.host); - - if (tuple.port !== null) { - result += ":" + tuple.port; - } - - return result; -} - -module.exports.serializeURL = serializeURL; - -module.exports.serializeURLOrigin = function (url) { - // https://url.spec.whatwg.org/#concept-url-origin - switch (url.scheme) { - case "blob": - try { - return module.exports.serializeURLOrigin(module.exports.parseURL(url.path[0])); - } catch (e) { - // serializing an opaque origin returns "null" - return "null"; - } - case "ftp": - case "gopher": - case "http": - case "https": - case "ws": - case "wss": - return serializeOrigin({ - scheme: url.scheme, - host: url.host, - port: url.port - }); - case "file": - // spec says "exercise to the reader", chrome says "file://" - return "file://"; - default: - // serializing an opaque origin returns "null" - return "null"; - } -}; - -module.exports.basicURLParse = function (input, options) { - if (options === undefined) { - options = {}; - } - - const usm = new URLStateMachine(input, options.baseURL, options.encodingOverride, options.url, options.stateOverride); - if (usm.failure) { - return "failure"; - } - - return usm.url; -}; - -module.exports.setTheUsername = function (url, username) { - url.username = ""; - const decoded = punycode.ucs2.decode(username); - for (let i = 0; i < decoded.length; ++i) { - url.username += percentEncodeChar(decoded[i], isUserinfoPercentEncode); - } -}; - -module.exports.setThePassword = function (url, password) { - url.password = ""; - const decoded = punycode.ucs2.decode(password); - for (let i = 0; i < decoded.length; ++i) { - url.password += percentEncodeChar(decoded[i], isUserinfoPercentEncode); - } -}; - -module.exports.serializeHost = serializeHost; - -module.exports.cannotHaveAUsernamePasswordPort = cannotHaveAUsernamePasswordPort; - -module.exports.serializeInteger = function (integer) { - return String(integer); -}; - -module.exports.parseURL = function (input, options) { - if (options === undefined) { - options = {}; - } - - // We don't handle blobs, so this just delegates: - return module.exports.basicURLParse(input, { baseURL: options.baseURL, encodingOverride: options.encodingOverride }); -}; - - -/***/ }), - -/***/ 3185: -/***/ ((module) => { - -"use strict"; - - -module.exports.mixin = function mixin(target, source) { - const keys = Object.getOwnPropertyNames(source); - for (let i = 0; i < keys.length; ++i) { - Object.defineProperty(target, keys[i], Object.getOwnPropertyDescriptor(source, keys[i])); - } -}; - -module.exports.wrapperSymbol = Symbol("wrapper"); -module.exports.implSymbol = Symbol("impl"); - -module.exports.wrapperForImpl = function (impl) { - return impl[module.exports.wrapperSymbol]; -}; - -module.exports.implForWrapper = function (wrapper) { - return wrapper[module.exports.implSymbol]; -}; - - - -/***/ }), - -/***/ 6059: -/***/ ((module) => { - -"use strict"; - - -var conversions = {}; -module.exports = conversions; - -function sign(x) { - return x < 0 ? -1 : 1; -} - -function evenRound(x) { - // Round x to the nearest integer, choosing the even integer if it lies halfway between two. - if ((x % 1) === 0.5 && (x & 1) === 0) { // [even number].5; round down (i.e. floor) - return Math.floor(x); - } else { - return Math.round(x); - } -} - -function createNumberConversion(bitLength, typeOpts) { - if (!typeOpts.unsigned) { - --bitLength; - } - const lowerBound = typeOpts.unsigned ? 0 : -Math.pow(2, bitLength); - const upperBound = Math.pow(2, bitLength) - 1; - - const moduloVal = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength) : Math.pow(2, bitLength); - const moduloBound = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength - 1) : Math.pow(2, bitLength - 1); - - return function(V, opts) { - if (!opts) opts = {}; - - let x = +V; - - if (opts.enforceRange) { - if (!Number.isFinite(x)) { - throw new TypeError("Argument is not a finite number"); - } - - x = sign(x) * Math.floor(Math.abs(x)); - if (x < lowerBound || x > upperBound) { - throw new TypeError("Argument is not in byte range"); - } - - return x; - } - - if (!isNaN(x) && opts.clamp) { - x = evenRound(x); - - if (x < lowerBound) x = lowerBound; - if (x > upperBound) x = upperBound; - return x; - } - - if (!Number.isFinite(x) || x === 0) { - return 0; - } - - x = sign(x) * Math.floor(Math.abs(x)); - x = x % moduloVal; - - if (!typeOpts.unsigned && x >= moduloBound) { - return x - moduloVal; - } else if (typeOpts.unsigned) { - if (x < 0) { - x += moduloVal; - } else if (x === -0) { // don't return negative zero - return 0; - } - } - - return x; - } -} - -conversions["void"] = function () { - return undefined; -}; - -conversions["boolean"] = function (val) { - return !!val; -}; - -conversions["byte"] = createNumberConversion(8, { unsigned: false }); -conversions["octet"] = createNumberConversion(8, { unsigned: true }); - -conversions["short"] = createNumberConversion(16, { unsigned: false }); -conversions["unsigned short"] = createNumberConversion(16, { unsigned: true }); - -conversions["long"] = createNumberConversion(32, { unsigned: false }); -conversions["unsigned long"] = createNumberConversion(32, { unsigned: true }); - -conversions["long long"] = createNumberConversion(32, { unsigned: false, moduloBitLength: 64 }); -conversions["unsigned long long"] = createNumberConversion(32, { unsigned: true, moduloBitLength: 64 }); - -conversions["double"] = function (V) { - const x = +V; - - if (!Number.isFinite(x)) { - throw new TypeError("Argument is not a finite floating-point value"); - } - - return x; -}; - -conversions["unrestricted double"] = function (V) { - const x = +V; - - if (isNaN(x)) { - throw new TypeError("Argument is NaN"); - } - - return x; -}; - -// not quite valid, but good enough for JS -conversions["float"] = conversions["double"]; -conversions["unrestricted float"] = conversions["unrestricted double"]; - -conversions["DOMString"] = function (V, opts) { - if (!opts) opts = {}; - - if (opts.treatNullAsEmptyString && V === null) { - return ""; - } - - return String(V); -}; - -conversions["ByteString"] = function (V, opts) { - const x = String(V); - let c = undefined; - for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) { - if (c > 255) { - throw new TypeError("Argument is not a valid bytestring"); - } - } - - return x; -}; - -conversions["USVString"] = function (V) { - const S = String(V); - const n = S.length; - const U = []; - for (let i = 0; i < n; ++i) { - const c = S.charCodeAt(i); - if (c < 0xD800 || c > 0xDFFF) { - U.push(String.fromCodePoint(c)); - } else if (0xDC00 <= c && c <= 0xDFFF) { - U.push(String.fromCodePoint(0xFFFD)); - } else { - if (i === n - 1) { - U.push(String.fromCodePoint(0xFFFD)); - } else { - const d = S.charCodeAt(i + 1); - if (0xDC00 <= d && d <= 0xDFFF) { - const a = c & 0x3FF; - const b = d & 0x3FF; - U.push(String.fromCodePoint((2 << 15) + (2 << 9) * a + b)); - ++i; - } else { - U.push(String.fromCodePoint(0xFFFD)); - } - } - } - } - - return U.join(''); -}; - -conversions["Date"] = function (V, opts) { - if (!(V instanceof Date)) { - throw new TypeError("Argument is not a Date object"); - } - if (isNaN(V)) { - return undefined; - } - - return V; -}; - -conversions["RegExp"] = function (V, opts) { - if (!(V instanceof RegExp)) { - V = new RegExp(V); - } - - return V; -}; - - -/***/ }), - -/***/ 2940: -/***/ ((module) => { - -// Returns a wrapper function that returns a wrapped callback -// The wrapper function should do some stuff, and return a -// presumably different callback function. -// This makes sure that own properties are retained, so that -// decorations and such are not lost along the way. -module.exports = wrappy -function wrappy (fn, cb) { - if (fn && cb) return wrappy(fn)(cb) - - if (typeof fn !== 'function') - throw new TypeError('need wrapper function') - - Object.keys(fn).forEach(function (k) { - wrapper[k] = fn[k] - }) - - return wrapper - - function wrapper() { - var args = new Array(arguments.length) - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i] - } - var ret = fn.apply(this, args) - var cb = args[args.length-1] - if (typeof ret === 'function' && ret !== cb) { - Object.keys(cb).forEach(function (k) { - ret[k] = cb[k] - }) - } - return ret - } -} - - -/***/ }), - -/***/ 566: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const core = __importStar(__nccwpck_require__(2186)); -const createOrUpdateStagingDeploy_1 = __importDefault(__nccwpck_require__(4287)); -async function main() { - try { - const token = core.getInput('GITHUB_TOKEN', { required: true }); - process.env.GITHUB_TOKEN = token; - await (0, createOrUpdateStagingDeploy_1.default)(); - } - catch (e) { - if (e instanceof Error) { - core.setFailed(e); - return; - } - core.setFailed('An unknown error occurred.'); - } -} -if (require.main === require.cache[eval('__filename')]) { - main(); -} -exports["default"] = createOrUpdateStagingDeploy_1.default; - - -/***/ }), - -/***/ 9873: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -const GITHUB_BASE_URL_REGEX = new RegExp('https?://(?:github\\.com|api\\.github\\.com)'); -const GIT_CONST = { - GITHUB_OWNER: process.env.GITHUB_REPOSITORY_OWNER ?? 'Expensify', - APP_REPO: (process.env.GITHUB_REPOSITORY ?? 'Expensify/App').split('/').at(1) ?? '', - MOBILE_EXPENSIFY_REPO: 'Mobile-Expensify', - DEFAULT_BASE_REF: 'main', -}; -const CONST = { - ...GIT_CONST, - APPLAUSE_BOT: 'applausebot', - OS_BOTIFY: 'OSBotify', - LABELS: { - STAGING_DEPLOY: 'StagingDeployCash', - DEPLOY_BLOCKER: 'DeployBlockerCash', - LOCK_DEPLOY: '🔐 LockCashDeploys 🔐', - INTERNAL_QA: 'InternalQA', - HELP_WANTED: 'Help Wanted', - CP_STAGING: 'CP Staging', - }, - STATE: { - OPEN: 'open', - }, - COMMENT: { - TYPE_BOT: 'Bot', - NAME_MELVIN: 'melvin-bot', - NAME_CODEX: 'chatgpt-codex-connector', - NAME_GITHUB_ACTIONS: 'github-actions', - }, - ACTIONS: { - CREATED: 'created', - EDITED: 'edited', - }, - EVENTS: { - ISSUE_COMMENT: 'issue_comment', - }, - RUN_EVENT: { - PULL_REQUEST: 'pull_request', - PULL_REQUEST_TARGET: 'pull_request_target', - PUSH: 'push', - }, - RUN_STATUS: { - COMPLETED: 'completed', - IN_PROGRESS: 'in_progress', - QUEUED: 'queued', - }, - RUN_STATUS_CONCLUSION: { - SUCCESS: 'success', - }, - TEST_WORKFLOW_NAME: 'Jest Unit Tests', - TEST_WORKFLOW_PATH: '.github/workflows/test.yml', - PROPOSAL_KEYWORD: 'Proposal', - PROPOSAL_HEADER_A: 'what is the root cause of that problem?', - PROPOSAL_HEADER_B: 'what changes do you think we should make in order to solve the problem?', - DATE_FORMAT_STRING: 'yyyy-MM-dd', - PULL_REQUEST_REGEX: new RegExp(`${GITHUB_BASE_URL_REGEX.source}/.*/.*/pull/([0-9]+).*`), - ISSUE_REGEX: new RegExp(`${GITHUB_BASE_URL_REGEX.source}/.*/.*/issues/([0-9]+).*`), - ISSUE_OR_PULL_REQUEST_REGEX: new RegExp(`${GITHUB_BASE_URL_REGEX.source}/.*/.*/(?:pull|issues)/([0-9]+).*`), - POLL_RATE: 10000, - APP_REPO_URL: `https://github.com/${GIT_CONST.GITHUB_OWNER}/${GIT_CONST.APP_REPO}`, - APP_REPO_GIT_URL: `git@github.com:${GIT_CONST.GITHUB_OWNER}/${GIT_CONST.APP_REPO}.git`, - MOBILE_EXPENSIFY_URL: `https://github.com/${GIT_CONST.GITHUB_OWNER}/${GIT_CONST.MOBILE_EXPENSIFY_REPO}`, - NO_ACTION: 'NO_ACTION', - ACTION_EDIT: 'ACTION_EDIT', - ACTION_REQUIRED: 'ACTION_REQUIRED', - ACTION_HIDE_DUPLICATE: 'ACTION_HIDE_DUPLICATE', -}; -exports["default"] = CONST; - - -/***/ }), - -/***/ 1547: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -const core = __importStar(__nccwpck_require__(2186)); -const child_process_1 = __nccwpck_require__(2081); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -const versionUpdater_1 = __nccwpck_require__(8982); -/** - * Check if a tag exists locally or in the remote. - */ -function tagExists(tag) { - try { - // Check if the tag exists locally - (0, child_process_1.execSync)(`git show-ref --tags ${tag}`, { stdio: 'ignore' }); - return true; // Tag exists locally - } - catch (error) { - // Tag does not exist locally, check in remote - let shouldRetry = true; - let needsRepack = false; - let doesTagExist = false; - while (shouldRetry) { - try { - if (needsRepack) { - // We have seen some scenarios where this fixes the git fetch. - // Why? Who knows... https://github.com/Expensify/App/pull/31459 - (0, child_process_1.execSync)('git repack -d', { stdio: 'inherit' }); - } - (0, child_process_1.execSync)(`git ls-remote --exit-code --tags origin ${tag}`, { stdio: 'ignore' }); - doesTagExist = true; - shouldRetry = false; - } - catch (e) { - if (!needsRepack) { - console.log('Attempting to repack and retry...'); - needsRepack = true; - } - else { - console.error("Repack didn't help, giving up..."); - shouldRetry = false; - } - } - } - return doesTagExist; - } -} -/** - * This essentially just calls getPreviousVersion in a loop, until it finds a version for which a tag exists. - * It's useful if we manually perform a version bump, because in that case a tag may not exist for the previous version. - * - * @param tag the current tag - * @param level the Semver level to step backward by - */ -function getPreviousExistingTag(tag, level) { - let previousVersion = (0, versionUpdater_1.getPreviousVersion)(tag.replace('-staging', ''), level); - let tagExistsForPreviousVersion = false; - while (!tagExistsForPreviousVersion) { - if (tagExists(previousVersion)) { - tagExistsForPreviousVersion = true; - break; - } - if (tagExists(`${previousVersion}-staging`)) { - tagExistsForPreviousVersion = true; - previousVersion = `${previousVersion}-staging`; - break; - } - console.log(`Tag for previous version ${previousVersion} does not exist. Checking for an older version...`); - previousVersion = (0, versionUpdater_1.getPreviousVersion)(previousVersion, level); - } - return previousVersion; -} -/** - * Extract Mobile-Expensify submodule update commits from the commit history. - * Matches both version-based ("Update Mobile-Expensify submodule version to 9.3.21-0") - * and hash-based ("Update Mobile-Expensify submodule to 9f18fca") patterns. - */ -function getSubmoduleUpdates(commits) { - const updates = []; - for (const commit of commits) { - const match = commit.subject.match(/^Update Mobile-Expensify submodule (?:version )?to (.+)$/); - if (match) { - updates.push({ - version: match[1], - date: commit.date, - commit: commit.commit, - }); - } - } - return updates; -} -/** - * Parse merged PRs, excluding those from irrelevant branches. - */ -function getValidMergedPRs(commits) { - const mergedPRs = new Map(); - for (const commit of commits) { - const author = commit.authorName; - if (author === CONST_1.default.OS_BOTIFY) { - continue; - } - // Retrieve the PR number from the commit subject, - const match = commit.subject.match(/Merge pull request #(\d+) from (?!Expensify\/.*-cherry-pick-(staging|production))/); - if (!Array.isArray(match) || match.length < 2) { - continue; - } - const pr = Number.parseInt(match[1], 10); - if (mergedPRs.has(pr)) { - // If a PR shows up in the log twice, that means that the PR was deployed in the previous checklist. - // That also means that we don't want to include it in the current checklist, so we remove it now. - mergedPRs.delete(pr); - continue; - } - mergedPRs.set(pr, commit.date); - } - return Array.from(mergedPRs.entries()).map(([prNumber, date]) => ({ prNumber, date })); -} -/** - * Takes in two git tags and returns a list of merged PRs entries between those two tags, - * along with any Mobile-Expensify submodule version updates found in the commit history. - * Returns PRs in the order they appear in the commit history from the GitHub API. - */ -async function getMergedPRsDeployedBetween(fromTag, toTag, repositoryName) { - console.log(`Looking for commits made between ${fromTag} and ${toTag}...`); - const apiCommitList = await GithubUtils_1.default.getCommitHistoryBetweenTags(fromTag, toTag, repositoryName); - const mergedPRs = getValidMergedPRs(apiCommitList); - const submoduleUpdates = getSubmoduleUpdates(apiCommitList); - console.log(`Found ${apiCommitList.length} commits.`); - core.startGroup('Parsed PRs:'); - core.info(mergedPRs.map((pr) => pr.prNumber).join(', ')); - core.endGroup(); - if (submoduleUpdates.length > 0) { - core.startGroup('Submodule updates:'); - core.info(submoduleUpdates.map((u) => u.version).join(', ')); - core.endGroup(); - } - return { mergedPRs, submoduleUpdates }; -} -/** - * Takes in two git tags and returns a list of PR numbers of all PRs merged between those two tags. - */ -async function getPullRequestsDeployedBetween(fromTag, toTag, repositoryName) { - const { mergedPRs } = await getMergedPRsDeployedBetween(fromTag, toTag, repositoryName); - return mergedPRs.map((pr) => pr.prNumber).sort((a, b) => a - b); -} -exports["default"] = { - getPreviousExistingTag, - getValidMergedPRs, - getSubmoduleUpdates, - getPullRequestsDeployedBetween, - getMergedPRsDeployedBetween, -}; - - -/***/ }), - -/***/ 9296: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -/* eslint-disable @typescript-eslint/naming-convention, import/no-import-module-exports */ -const core = __importStar(__nccwpck_require__(2186)); -const utils_1 = __nccwpck_require__(3030); -const plugin_paginate_rest_1 = __nccwpck_require__(4193); -const plugin_throttling_1 = __nccwpck_require__(9968); -const request_error_1 = __nccwpck_require__(537); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -class GithubUtils { - static internalOctokit; - /** - * Initialize internal octokit. - * NOTE: When using GithubUtils in CI, you don't need to call this manually. - */ - static initOctokitWithToken(token) { - const Octokit = utils_1.GitHub.plugin(plugin_throttling_1.throttling, plugin_paginate_rest_1.paginateRest); - // Save a copy of octokit used in this class - this.internalOctokit = new Octokit((0, utils_1.getOctokitOptions)(token, { - throttle: { - retryAfterBaseValue: 2000, - onRateLimit: (retryAfter, options) => { - console.warn(`Request quota exhausted for request ${options.method} ${options.url}`); - // Retry five times when hitting a rate limit error, then give up - if (options.request.retryCount <= 5) { - console.log(`Retrying after ${retryAfter} seconds!`); - return true; - } - }, - onAbuseLimit: (retryAfter, options) => { - // does not retry, only logs a warning - console.warn(`Abuse detected for request ${options.method} ${options.url}`); - }, - }, - })); - } - /** - * Default initialize method assuming running in CI, getting the token from an input. - * - * @private - */ - static initOctokit() { - const token = process.env.GITHUB_TOKEN ?? core.getInput('GITHUB_TOKEN', { required: true }); - if (!token) { - console.error('GitHubUtils could not find GITHUB_TOKEN'); - process.exit(1); - } - this.initOctokitWithToken(token); - } - /** - * Either give an existing instance of Octokit rest or create a new one - * - * @readonly - * @static - */ - static get octokit() { - if (!this.internalOctokit) { - this.initOctokit(); - } - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - return this.internalOctokit.rest; - } - /** - * Get the graphql instance from internal octokit. - * @readonly - * @static - */ - static get graphql() { - if (!this.internalOctokit) { - this.initOctokit(); - } - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - return this.internalOctokit.graphql; - } - /** - * Either give an existing instance of Octokit paginate or create a new one - * - * @readonly - * @static - */ - static get paginate() { - if (!this.internalOctokit) { - this.initOctokit(); - } - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - return this.internalOctokit.paginate; - } - /** - * Fetch all pull requests given a list of PR numbers. - */ - static fetchAllPullRequests(pullRequestNumbers) { - const oldestPR = pullRequestNumbers.sort((a, b) => a - b).at(0); - return this.paginate(this.octokit.pulls.list, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - state: 'all', - sort: 'created', - direction: 'desc', - per_page: 100, - }, ({ data }, done) => { - if (data.find((pr) => pr.number === oldestPR)) { - done(); - } - return data; - }) - .then((prList) => prList?.filter((pr) => pullRequestNumbers.includes(pr.number)) ?? []) - .catch((err) => console.error('Failed to get PR list', err)); - } - static getPullRequestMergerLogin(pullRequestNumber) { - return this.octokit.pulls - .get({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - pull_number: pullRequestNumber, - }) - .then(({ data: pullRequest }) => pullRequest.merged_by?.login); - } - static getPullRequestBody(pullRequestNumber) { - return this.octokit.pulls - .get({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - pull_number: pullRequestNumber, - }) - .then(({ data: pullRequestComment }) => pullRequestComment.body); - } - static getAllReviewComments(pullRequestNumber) { - return this.paginate(this.octokit.pulls.listReviews, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - pull_number: pullRequestNumber, - per_page: 100, - }, (response) => response.data.map((review) => review.body)); - } - static getAllComments(issueNumber) { - return this.paginate(this.octokit.issues.listComments, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - issue_number: issueNumber, - per_page: 100, - }, (response) => response.data.map((comment) => comment.body)); - } - static getAllCommentDetails(issueNumber) { - return this.paginate(this.octokit.issues.listComments, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - issue_number: issueNumber, - per_page: 100, - }, (response) => response.data); - } - /** - * Create comment on pull request - */ - static createComment(repo, number, messageBody) { - console.log(`Writing comment on #${number}`); - return this.octokit.issues.createComment({ - owner: CONST_1.default.GITHUB_OWNER, - repo, - issue_number: number, - body: messageBody, - }); - } - /** - * Get the most recent workflow run for the given New Expensify workflow. - */ - /* eslint-disable rulesdir/no-default-id-values */ - static getLatestWorkflowRunID(workflow) { - console.log(`Fetching New Expensify workflow runs for ${workflow}...`); - return this.octokit.actions - .listWorkflowRuns({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - workflow_id: workflow, - }) - .then((response) => response.data.workflow_runs.at(0)?.id ?? -1); - } - /** - * List workflow runs for the repository. - */ - static async listWorkflowRunsForRepo(options = {}) { - return this.octokit.actions.listWorkflowRunsForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - per_page: options.per_page ?? 50, - ...(options.status && { status: options.status }), - }); - } - /** - * Get the workflow run URL for a specific commit SHA and workflow file. - * Returns the HTML URL of the matching run, or undefined if not found. - */ - static async getWorkflowRunURLForCommit(commitSha, workflowFile) { - try { - const response = await this.octokit.actions.listWorkflowRuns({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - workflow_id: workflowFile, - head_sha: commitSha, - per_page: 1, - }); - return response.data.workflow_runs.at(0)?.html_url; - } - catch (error) { - console.warn(`Failed to find workflow run for commit ${commitSha}:`, error); - return undefined; - } - } - /** - * Generate the URL of an New Expensify pull request given the PR number. - */ - static getPullRequestURLFromNumber(value, repositoryURL) { - return `${repositoryURL}/pull/${value}`; - } - /** - * Parse the pull request number from a URL. - * - * @throws {Error} If the URL is not a valid Github Pull Request. - */ - static getPullRequestNumberFromURL(URL) { - const matches = URL.match(CONST_1.default.PULL_REQUEST_REGEX); - if (!Array.isArray(matches) || matches.length !== 2) { - throw new Error(`Provided URL ${URL} is not a Github Pull Request!`); - } - return Number.parseInt(matches[1], 10); - } - /** - * Parse the issue number from a URL. - * - * @throws {Error} If the URL is not a valid Github Issue. - */ - static getIssueNumberFromURL(URL) { - const matches = URL.match(CONST_1.default.ISSUE_REGEX); - if (!Array.isArray(matches) || matches.length !== 2) { - throw new Error(`Provided URL ${URL} is not a Github Issue!`); - } - return Number.parseInt(matches[1], 10); - } - /** - * Parse the issue or pull request number from a URL. - * - * @throws {Error} If the URL is not a valid Github Issue or Pull Request. - */ - static getIssueOrPullRequestNumberFromURL(URL) { - const matches = URL.match(CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX); - if (!Array.isArray(matches) || matches.length !== 2) { - throw new Error(`Provided URL ${URL} is not a valid Github Issue or Pull Request!`); - } - return Number.parseInt(matches[1], 10); - } - /** - * Return the login of the actor who closed an issue or PR. If the issue is not closed, return an empty string. - */ - static getActorWhoClosedIssue(issueNumber) { - return this.paginate(this.octokit.issues.listEvents, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - issue_number: issueNumber, - per_page: 100, - }) - .then((events) => events.filter((event) => event.event === 'closed')) - .then((closedEvents) => closedEvents.at(-1)?.actor?.login ?? ''); - } - /** - * Returns a single artifact by name. If none is found, it returns undefined. - */ - static getArtifactByName(artifactName) { - return this.octokit.actions - .listArtifactsForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - per_page: 1, - name: artifactName, - }) - .then((response) => response.data.artifacts.at(0)); - } - /** - * Given an artifact ID, returns the download URL to a zip file containing the artifact. - */ - static getArtifactDownloadURL(artifactId) { - return this.octokit.actions - .downloadArtifact({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - artifact_id: artifactId, - archive_format: 'zip', - }) - .then((response) => response.url); - } - /** - * Get the contents of a file from the API at a given ref as a string. - */ - static async getFileContents(path, ref = CONST_1.default.DEFAULT_BASE_REF) { - const { data } = await this.octokit.repos.getContent({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - path, - ref, - }); - if (Array.isArray(data)) { - throw new Error(`Provided path ${path} refers to a directory, not a file`); - } - if (!('content' in data)) { - throw new Error(`Provided path ${path} is invalid`); - } - return Buffer.from(data.content, 'base64').toString('utf8'); - } - static async getPullRequestChangedSVGFileNames(pullRequestNumber) { - const files = this.paginate(this.octokit.pulls.listFiles, { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - pull_number: pullRequestNumber, - per_page: 100, - }, (response) => response.data.filter((file) => file.filename.endsWith('.svg') && (file.status === 'added' || file.status === 'modified')).map((file) => file.filename)); - return files; - } - /** - * Get commits between two tags via the GitHub API - */ - static async getCommitHistoryBetweenTags(fromTag, toTag, repositoryName) { - console.log('Getting pull requests merged between the following tags:', fromTag, toTag); - core.startGroup('Fetching paginated commits:'); - try { - let allCommits = []; - let page = 1; - const perPage = 250; - let hasMorePages = true; - while (hasMorePages) { - core.info(`📄 Fetching page ${page} of commits...`); - const response = await this.octokit.repos.compareCommits({ - owner: CONST_1.default.GITHUB_OWNER, - repo: repositoryName, - base: fromTag, - head: toTag, - per_page: perPage, - page, - }); - // Check if we got a proper response with commits - if (response.data?.commits && Array.isArray(response.data.commits)) { - if (page === 1) { - core.info(`📊 Total commits: ${response.data.total_commits ?? 'unknown'}`); - } - core.info(`✅ compareCommits API returned ${response.data.commits.length} commits for page ${page}`); - allCommits = allCommits.concat(response.data.commits); - // Check if we got fewer commits than requested or if we've reached the total - const totalCommits = response.data.total_commits; - if (response.data.commits.length < perPage || (totalCommits && allCommits.length >= totalCommits)) { - hasMorePages = false; - } - else { - page++; - } - } - else { - core.warning('⚠️ GitHub API returned unexpected response format'); - hasMorePages = false; - } - } - core.info(`🎉 Successfully fetched ${allCommits.length} total commits`); - core.endGroup(); - console.log(''); - return allCommits.map((commit) => ({ - commit: commit.sha, - subject: commit.commit.message, - authorName: commit.commit.author?.name ?? 'Unknown', - date: commit.commit.committer?.date ?? '', - })); - } - catch (error) { - if (error instanceof request_error_1.RequestError && error.status === 404) { - core.error(`❓❓ Failed to get commits with the GitHub API. The base tag ('${fromTag}') or head tag ('${toTag}') likely doesn't exist on the remote repository. If this is the case, create or push them.`); - } - core.endGroup(); - console.log(''); - throw error; - } - } - static async getPullRequestDiff(pullRequestNumber) { - if (!this.internalOctokit) { - this.initOctokit(); - } - // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style - const response = await this.internalOctokit.request('GET /repos/{owner}/{repo}/pulls/{pull_number}', { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - pull_number: pullRequestNumber, - mediaType: { - format: 'diff', - }, - }); - return response.data; - } -} -exports["default"] = GithubUtils; - - -/***/ }), - -/***/ 3318: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getStagingDeployCash = getStagingDeployCash; -exports.getStagingDeployCashData = getStagingDeployCashData; -exports.generateStagingDeployCashBodyAndAssignees = generateStagingDeployCashBodyAndAssignees; -exports.parseChecklistSection = parseChecklistSection; -const dedent_1 = __importDefault(__nccwpck_require__(6762)); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -const isEmptyObject_1 = __nccwpck_require__(6497); -/** - * Generic checklist section parser. Extracts a section from the issue body, - * parses checkbox items within it, and returns typed objects sorted by number. - */ -function parseChecklistSection(issueBody, sectionRegex, itemRegex, checkedKey, urlTransform) { - const sectionMatch = issueBody?.match(sectionRegex) ?? null; - if (sectionMatch?.length !== 2) { - return []; - } - const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { - const rawUrl = match[2]; - return { - url: urlTransform ? urlTransform(rawUrl) : rawUrl, - number: Number.parseInt(match[3], 10), - [checkedKey]: match[1] === 'x', - }; - }); - return items.sort((a, b) => a.number - b.number); -} -function getStagingDeployCashPRList(issue) { - const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); - if (result.length === 0) { - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - } - return result; -} -function getStagingDeployCashPRListMobileExpensify(issue) { - return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); -} -function getStagingDeployCashDeployBlockers(issue) { - return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); -} -function getStagingDeployCashInternalQA(issue) { - return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved', (url) => url.split('-').at(0)?.trim() ?? ''); -} -async function getStagingDeployCash() { - const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }); - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return getStagingDeployCashData(issue); -} -function getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: getStagingDeployCashPRList(issue), - PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: getStagingDeployCashDeployBlockers(issue), - internalQAPRList: getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } -} -/** - * Generate the issue body and assignees for a StagingDeployCash checklist. - * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. - */ -async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; - const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); - const internalQAPRMap = new Map(); - for (const { number, mergerLogin } of mergerResults) { - internalQAPRMap.set(number, mergerLogin); - } - console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); - const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; - console.log('Found the following NO QA PRs:', noQAPRNumbers); - const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); - const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); - const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); - const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); - const internalQAPRNumbers = new Set(internalQAPRMap.keys()); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(a) - GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(b)); - const sections = []; - // Header - let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; - if (sortedPRListMobileExpensify.length > 0) { - header += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; - } - sections.push(header); - // PR list - if (sortedPRList.length > 0) { - const items = sortedPRList - .map((prNumber) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); - return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; - }) - .join('\n'); - sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - const items = sortedPRListMobileExpensify - .map((prNumber) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; - }) - .join('\n'); - sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); - } - // Internal QA PR list - if (internalQAPRMap.size > 0) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - const items = [...internalQAPRMap.entries()] - .map(([prNumber, merger]) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); - return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; - }) - .join('\n'); - sections.push(`**Internal QA:**\n${items}\n`); - } - // Deploy blockers - if (deployBlockers.length > 0) { - const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); - sections.push(`**Deploy Blockers:**\n${items}\n`); - } - // Chronological section - if (chronologicalSection) { - sections.push(chronologicalSection); - } - // Deployer verifications - const check = (checked) => (checked ? 'x' : ' '); - sections.push((0, dedent_1.default)(` - **Deployer verifications:** - - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). - - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). - - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. - `).trimEnd()); - // Footer - sections.push('cc @Expensify/applauseleads\n'); - const issueBody = sections.join('\n'); - const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login) => login !== undefined); - return { issueBody, issueAssignees }; -} - - -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - -/***/ }), - -/***/ 8982: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.incrementPatch = exports.incrementMinor = exports.SEMANTIC_VERSION_LEVELS = exports.MAX_INCREMENTS = exports.incrementVersion = exports.getVersionStringFromNumber = exports.getVersionNumberFromString = void 0; -exports.isValidSemverLevel = isValidSemverLevel; -exports.getPreviousVersion = getPreviousVersion; -const SEMANTIC_VERSION_LEVELS = { - MAJOR: 'MAJOR', - MINOR: 'MINOR', - PATCH: 'PATCH', - BUILD: 'BUILD', -}; -exports.SEMANTIC_VERSION_LEVELS = SEMANTIC_VERSION_LEVELS; -const MAX_INCREMENTS = 99; -exports.MAX_INCREMENTS = MAX_INCREMENTS; -function isValidSemverLevel(str) { - return Object.keys(SEMANTIC_VERSION_LEVELS).includes(str); -} -/** - * Transforms a versions string into a number - */ -const getVersionNumberFromString = (versionString) => { - const [version, build] = versionString.split('-'); - const [major, minor, patch] = version.split('.').map((n) => Number(n)); - return [major, minor, patch, Number.isInteger(Number(build)) ? Number(build) : 0]; -}; -exports.getVersionNumberFromString = getVersionNumberFromString; -/** - * Transforms version numbers components into a version string - */ -const getVersionStringFromNumber = (major, minor, patch, build = 0) => `${major}.${minor}.${patch}-${build}`; -exports.getVersionStringFromNumber = getVersionStringFromNumber; -/** - * Increments a minor version - */ -const incrementMinor = (major, minor) => { - if (minor < MAX_INCREMENTS) { - return getVersionStringFromNumber(major, minor + 1, 0, 0); - } - return getVersionStringFromNumber(major + 1, 0, 0, 0); -}; -exports.incrementMinor = incrementMinor; -/** - * Increments a Patch version - */ -const incrementPatch = (major, minor, patch) => { - if (patch < MAX_INCREMENTS) { - return getVersionStringFromNumber(major, minor, patch + 1, 0); - } - return incrementMinor(major, minor); -}; -exports.incrementPatch = incrementPatch; -/** - * Increments a build version - */ -const incrementVersion = (version, level) => { - const [major, minor, patch, build] = getVersionNumberFromString(version); - // Majors will always be incremented - if (level === SEMANTIC_VERSION_LEVELS.MAJOR) { - return getVersionStringFromNumber(major + 1, 0, 0, 0); - } - if (level === SEMANTIC_VERSION_LEVELS.MINOR) { - return incrementMinor(major, minor); - } - if (level === SEMANTIC_VERSION_LEVELS.PATCH) { - return incrementPatch(major, minor, patch); - } - if (build < MAX_INCREMENTS) { - return getVersionStringFromNumber(major, minor, patch, build + 1); - } - return incrementPatch(major, minor, patch); -}; -exports.incrementVersion = incrementVersion; -function getPreviousVersion(currentVersion, level) { - const [major, minor, patch, build] = getVersionNumberFromString(currentVersion); - if (level === SEMANTIC_VERSION_LEVELS.MAJOR) { - if (major === 1) { - return getVersionStringFromNumber(1, 0, 0, 0); - } - return getVersionStringFromNumber(major - 1, 0, 0, 0); - } - if (level === SEMANTIC_VERSION_LEVELS.MINOR) { - if (minor === 0) { - return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MAJOR); - } - return getVersionStringFromNumber(major, minor - 1, 0, 0); - } - if (level === SEMANTIC_VERSION_LEVELS.PATCH) { - if (patch === 0) { - return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.MINOR); - } - return getVersionStringFromNumber(major, minor, patch - 1, 0); - } - if (build === 0) { - return getPreviousVersion(currentVersion, SEMANTIC_VERSION_LEVELS.PATCH); - } - return getVersionStringFromNumber(major, minor, patch, build - 1); -} - - -/***/ }), - -/***/ 4287: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.buildChronologicalSection = buildChronologicalSection; -exports.buildNewChecklistParams = buildNewChecklistParams; -exports.buildUpdateChecklistParams = buildUpdateChecklistParams; -exports.preserveCheckboxState = preserveCheckboxState; -/* eslint-disable @typescript-eslint/naming-convention */ -const core = __importStar(__nccwpck_require__(2186)); -const format_1 = __nccwpck_require__(2464); -const fs_1 = __importDefault(__nccwpck_require__(7147)); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -const GitUtils_1 = __importDefault(__nccwpck_require__(1547)); -const StagingDeployUtils_1 = __nccwpck_require__(3318); -/** - * Preserve the checked state (isVerified/isResolved) from a previous checklist when building a new one. - */ -function preserveCheckboxState(items, currentList, checkedKey) { - return items.map((item) => { - const existing = currentList?.find((c) => c.number === item.number); - return { ...item, [checkedKey]: existing ? existing[checkedKey] : false }; - }); -} -async function buildChronologicalSection({ chronologicalPREntries, submoduleUpdates, mergedMobileExpensifyPREntries, }) { - if (chronologicalPREntries.length === 0 && submoduleUpdates.length === 0 && mergedMobileExpensifyPREntries.length === 0) { - return ''; - } - const submoduleRunURLs = new Map(); - const results = await Promise.allSettled(submoduleUpdates.map(async (update) => { - const runURL = await GithubUtils_1.default.getWorkflowRunURLForCommit(update.commit, 'testBuildOnPush.yml'); - return { commit: update.commit, runURL }; - })); - for (const result of results) { - if (result.status === 'fulfilled') { - submoduleRunURLs.set(result.value.commit, result.value.runURL); - } - } - const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); - const mobileExpensifyPRsBySubmodule = new Map(); - const mobileExpensifyPRsPendingSubmoduleUpdate = []; - for (const mobileExpensifyPR of mergedMobileExpensifyPREntries) { - const matchingUpdate = sortedSubmoduleUpdates.find((update) => update.date.localeCompare(mobileExpensifyPR.date) >= 0); - if (matchingUpdate) { - const existing = mobileExpensifyPRsBySubmodule.get(matchingUpdate.commit) ?? []; - existing.push(mobileExpensifyPR); - mobileExpensifyPRsBySubmodule.set(matchingUpdate.commit, existing); - } - else { - mobileExpensifyPRsPendingSubmoduleUpdate.push(mobileExpensifyPR); - } - } - const timeline = [ - ...chronologicalPREntries.map((pr) => ({ type: 'pr', prNumber: pr.prNumber, date: pr.date })), - ...submoduleUpdates.map((update) => ({ type: 'submodule', version: update.version, date: update.date, commit: update.commit })), - ].sort((a, b) => a.date.localeCompare(b.date)); - let section = '
\nChronologically ordered merged PRs (oldest first)\n\n'; - let prIndex = 0; - for (const entry of timeline) { - if (entry.type === 'submodule') { - prIndex++; - const runURL = submoduleRunURLs.get(entry.commit); - const buildLink = runURL ? ` — [Adhoc Build](${runURL})` : ` — ${entry.commit.substring(0, 7)}`; - section += `${prIndex}. Mobile-Expensify submodule update to \`${entry.version}\`${buildLink}\n`; - const mobileExpensifyPRs = mobileExpensifyPRsBySubmodule.get(entry.commit); - if (mobileExpensifyPRs) { - const sortedMobileExpensifyPRs = [...mobileExpensifyPRs].sort((a, b) => a.date.localeCompare(b.date)); - for (const mobileExpensifyPR of sortedMobileExpensifyPRs) { - const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - section += ` ↳ ${mobileExpensifyUrl}\n`; - } - } - } - else { - prIndex++; - const url = GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.APP_REPO_URL); - section += `${prIndex}. ${url}\n`; - } - } - if (mobileExpensifyPRsPendingSubmoduleUpdate.length > 0) { - const sortedPending = [...mobileExpensifyPRsPendingSubmoduleUpdate].sort((a, b) => a.date.localeCompare(b.date)); - section += `\n--- PRs waiting for Mobile-Expensify submodule update\n`; - for (const mobileExpensifyPR of sortedPending) { - const mobileExpensifyUrl = GithubUtils_1.default.getPullRequestURLFromNumber(mobileExpensifyPR.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - section += `${mobileExpensifyUrl}\n`; - } - } - section += '\n
'; - return section; -} -/** - * Build params for creating a brand-new deploy checklist. - */ -function buildNewChecklistParams({ newVersion, newPRNumbers, mergedMobileExpensifyPREntries, previousChecklistData, chronologicalSection, }) { - return { - tag: newVersion, - PRList: newPRNumbers, - PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => pr.prNumber), - previousTag: previousChecklistData.version, - chronologicalSection, - }; -} -/** - * Build params for updating an existing open deploy checklist, preserving verified/resolved state. - */ -async function buildUpdateChecklistParams({ newVersion, newPRNumbers, mergedMobileExpensifyPREntries, previousChecklistData, currentChecklistData, chronologicalSection, }) { - const PRList = preserveCheckboxState(newPRNumbers.map((prNum) => ({ - number: prNum, - url: GithubUtils_1.default.getPullRequestURLFromNumber(prNum, CONST_1.default.APP_REPO_URL), - })), currentChecklistData.PRList, 'isVerified'); - const PRListMobileExpensify = preserveCheckboxState(mergedMobileExpensifyPREntries.map((entry) => ({ - number: entry.prNumber, - url: GithubUtils_1.default.getPullRequestURLFromNumber(entry.prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL), - })), currentChecklistData.PRListMobileExpensify, 'isVerified'); - // Include existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list - for (const existingPR of currentChecklistData.PRListMobileExpensify) { - if (!PRListMobileExpensify.some((pr) => pr.number === existingPR.number)) { - PRListMobileExpensify.push(existingPR); - } - } - const openDeployBlockers = await GithubUtils_1.default.paginate(GithubUtils_1.default.octokit.issues.listForRepo, { - log: console, - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.DEPLOY_BLOCKER, - }); - const deployBlockers = preserveCheckboxState(openDeployBlockers.map((db) => ({ number: db.number, url: db.html_url })), currentChecklistData.deployBlockers, 'isResolved'); - // Add demoted/closed blockers from current checklist and auto-check them - for (const deployBlocker of currentChecklistData.deployBlockers) { - const isResolved = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); - deployBlockers.push({ ...deployBlocker, isResolved }); - } - const didVersionChange = newVersion !== currentChecklistData.version; - return { - tag: newVersion, - PRList: PRList.map((pr) => pr.number), - PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.number), - verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.number), - verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.number), - deployBlockers: deployBlockers.map((blocker) => blocker.url), - resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), - resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.number), - isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, - isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, - previousTag: previousChecklistData.version, - chronologicalSection, - }; -} -async function run() { - // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time - const packageJson = JSON.parse(fs_1.default.readFileSync('package.json', 'utf8')); - const newVersion = packageJson.version; - const newStagingTag = `${packageJson.version}-staging`; - try { - const { data: recentDeployChecklists } = await GithubUtils_1.default.octokit.issues.listForRepo({ - log: console, - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'all', - }); - const mostRecentChecklist = recentDeployChecklists.at(0); - if (!mostRecentChecklist) { - throw new Error('Could not find the most recent checklist'); - } - const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; - const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); - if (shouldCreateNewDeployChecklist) { - console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); - } - else { - console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); - } - if (!previousChecklist) { - throw new Error('Could not find the previous checklist'); - } - const previousChecklistData = (0, StagingDeployUtils_1.getStagingDeployCashData)(previousChecklist); - const currentChecklistData = shouldCreateNewDeployChecklist ? undefined : (0, StagingDeployUtils_1.getStagingDeployCashData)(mostRecentChecklist); - const { mergedPRs: mergedPREntries, submoduleUpdates } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.APP_REPO); - const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); - const previousPRNumbers = new Set(previousChecklistData.PRList.map((pr) => pr.number)); - const previousMobileExpensifyPRNumbers = new Set(previousChecklistData.PRListMobileExpensify.map((pr) => pr.number)); - core.startGroup('Filtering PRs:'); - core.info('mergedPRs includes cherry-picked PRs that have already been released with previous checklist, so we need to filter these out'); - core.info(`Found ${previousPRNumbers.size} PRs in the previous checklist:`); - core.info(JSON.stringify(Array.from(previousPRNumbers))); - const newPRNumbers = mergedPRs.filter((prNum) => !previousPRNumbers.has(prNum)); - core.info(`Found ${newPRNumbers.length} PRs deployed since the previous checklist:`); - core.info(JSON.stringify(newPRNumbers)); - const removedPRs = mergedPRs.filter((prNum) => previousPRNumbers.has(prNum)); - if (removedPRs.length > 0) { - core.info(`⚠️⚠️ Filtered out the following cherry-picked PRs that were released with the previous checklist: ${removedPRs.join(', ')} ⚠️⚠️`); - } - core.endGroup(); - console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); - let mergedMobileExpensifyPREntries = []; - try { - const { mergedPRs: allMobileExpensifyPREntries } = await GitUtils_1.default.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST_1.default.MOBILE_EXPENSIFY_REPO); - mergedMobileExpensifyPREntries = allMobileExpensifyPREntries.filter((pr) => !previousMobileExpensifyPRNumbers.has(pr.prNumber)); - const allCount = allMobileExpensifyPREntries.length; - const newCount = mergedMobileExpensifyPREntries.length; - console.info(`Found ${allCount} total Mobile-Expensify PRs, ${newCount} new ones after filtering:`); - console.info(`Mobile-Expensify PRs: ${mergedMobileExpensifyPREntries.map((pr) => pr.prNumber).join(', ')}`); - const removedMobileExpensifyPRs = allMobileExpensifyPREntries.filter((pr) => previousMobileExpensifyPRNumbers.has(pr.prNumber)); - if (removedMobileExpensifyPRs.length > 0) { - core.info(`⚠️⚠️ Filtered out the following cherry-picked Mobile-Expensify PRs that were released with the previous checklist: ${removedMobileExpensifyPRs.map((pr) => pr.prNumber).join(', ')} ⚠️⚠️`); - } - } - catch (error) { - if (process.env.GITHUB_REPOSITORY !== `${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.APP_REPO}`) { - console.warn("⚠️ Unable to fetch Mobile-Expensify PRs because this workflow is running on a forked repository and secrets aren't accessible. This is expected for development/testing on forks."); - } - else { - console.error('Failed to fetch Mobile-Expensify PRs:', error); - } - } - const chronologicalPREntries = mergedPREntries.filter((pr) => !previousPRNumbers.has(pr.prNumber)).sort((a, b) => a.date.localeCompare(b.date)); - const chronologicalSection = await buildChronologicalSection({ - chronologicalPREntries, - submoduleUpdates, - mergedMobileExpensifyPREntries, - }); - let checklistParams; - if (shouldCreateNewDeployChecklist) { - checklistParams = buildNewChecklistParams({ - newVersion, - newPRNumbers, - mergedMobileExpensifyPREntries, - previousChecklistData, - chronologicalSection, - }); - } - else if (currentChecklistData) { - checklistParams = await buildUpdateChecklistParams({ - newVersion, - newPRNumbers, - mergedMobileExpensifyPREntries, - previousChecklistData, - currentChecklistData, - chronologicalSection, - }); - } - else { - throw new Error('Expected current checklist data for update but it was undefined'); - } - const { issueBody: checklistBody, issueAssignees: checklistAssignees } = await (0, StagingDeployUtils_1.generateStagingDeployCashBodyAndAssignees)(checklistParams); - const defaultPayload = { - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - body: checklistBody, - }; - if (shouldCreateNewDeployChecklist) { - const { data: newChecklist } = await GithubUtils_1.default.octokit.issues.create({ - ...defaultPayload, - title: `Deploy Checklist: New Expensify ${(0, format_1.format)(new Date(), CONST_1.default.DATE_FORMAT_STRING)}`, - labels: [CONST_1.default.LABELS.STAGING_DEPLOY, CONST_1.default.LABELS.LOCK_DEPLOY], - assignees: [CONST_1.default.APPLAUSE_BOT].concat(checklistAssignees), - }); - console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); - return newChecklist; - } - const { data: updatedChecklist } = await GithubUtils_1.default.octokit.issues.update({ - ...defaultPayload, - issue_number: currentChecklistData?.number ?? 0, - }); - console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); - return updatedChecklist; - } - catch (err) { - console.error('An unknown error occurred!', err); - core.setFailed(err); - } -} -if (false) {} -exports["default"] = run; - - -/***/ }), - -/***/ 6762: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -/* - * Note: This file is separated from StringUtils because it is imported by a ts-node script. - * ts-node scripts can't import react-native (because it is written in flow), - * and StringUtils indirectly imports react-native. - */ -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports["default"] = dedent; -/** - * Find the minimum indentation of any line in the string, - * and remove that number of leading spaces from every line in the string. - * - * It also removes at most one leading newline, to reflect a common usage: - * - * ``` - * StringUtils.dedent(` - * const myIndentedStr = 'Hello, world!'; - * console.log(myIndentedStr); - * `) - * ``` - * - * This implementation assumes you'd want that to be: - * - * ``` - * const myIndentedStr = 'Hello, world!'; - * console.log(myIndentedStr); - * - * ``` - * - * Rather than: - * - * ``` - * - * const myIndentedStr = 'Hello, world!'; - * console.log(myIndentedStr); - * - * ``` - */ -function dedent(str) { - // Remove at most one leading newline - const stringWithoutLeadingNewlines = str.replaceAll(/^\r?\n/g, ''); - // Split string by remaining newlines - const lines = stringWithoutLeadingNewlines.replaceAll('\r\n', '\n').split('\n'); - // Find the minimum indentation of non-empty lines - let minIndent = Number.MAX_SAFE_INTEGER; - for (const line of lines) { - if (line.trim().length === 0) { - continue; - } - const indentation = line.match(/^ */)?.[0].length ?? 0; - if (indentation < minIndent) { - minIndent = indentation; - } - } - // Remove the common indentation - return lines.map((line) => line.slice(minIndent)).join('\n'); -} - - -/***/ }), - -/***/ 9491: -/***/ ((module) => { - -"use strict"; -module.exports = require("assert"); - -/***/ }), - -/***/ 2081: -/***/ ((module) => { - -"use strict"; -module.exports = require("child_process"); - -/***/ }), - -/***/ 6113: -/***/ ((module) => { - -"use strict"; -module.exports = require("crypto"); - -/***/ }), - -/***/ 3975: -/***/ ((module) => { - -"use strict"; -module.exports = require("encoding"); - -/***/ }), - -/***/ 2361: -/***/ ((module) => { - -"use strict"; -module.exports = require("events"); - -/***/ }), - -/***/ 7147: -/***/ ((module) => { - -"use strict"; -module.exports = require("fs"); - -/***/ }), - -/***/ 3685: -/***/ ((module) => { - -"use strict"; -module.exports = require("http"); - -/***/ }), - -/***/ 5687: -/***/ ((module) => { - -"use strict"; -module.exports = require("https"); - -/***/ }), - -/***/ 1808: -/***/ ((module) => { - -"use strict"; -module.exports = require("net"); - -/***/ }), - -/***/ 2037: -/***/ ((module) => { - -"use strict"; -module.exports = require("os"); - -/***/ }), - -/***/ 1017: -/***/ ((module) => { - -"use strict"; -module.exports = require("path"); - -/***/ }), - -/***/ 5477: -/***/ ((module) => { - -"use strict"; -module.exports = require("punycode"); - -/***/ }), - -/***/ 2781: -/***/ ((module) => { - -"use strict"; -module.exports = require("stream"); - -/***/ }), - -/***/ 4404: -/***/ ((module) => { - -"use strict"; -module.exports = require("tls"); - -/***/ }), - -/***/ 7310: -/***/ ((module) => { - -"use strict"; -module.exports = require("url"); - -/***/ }), - -/***/ 3837: -/***/ ((module) => { - -"use strict"; -module.exports = require("util"); - -/***/ }), - -/***/ 9796: -/***/ ((module) => { - -"use strict"; -module.exports = require("zlib"); - -/***/ }), - -/***/ 6411: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.addLeadingZeros = addLeadingZeros; -function addLeadingZeros(number, targetLength) { - const sign = number < 0 ? "-" : ""; - const output = Math.abs(number).toString().padStart(targetLength, "0"); - return sign + output; -} - - -/***/ }), - -/***/ 7479: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -Object.defineProperty(exports, "defaultLocale", ({ - enumerable: true, - get: function () { - return _index.enUS; - }, -})); -var _index = __nccwpck_require__(9425); - - -/***/ }), - -/***/ 5586: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.getDefaultOptions = getDefaultOptions; -exports.setDefaultOptions = setDefaultOptions; - -let defaultOptions = {}; - -function getDefaultOptions() { - return defaultOptions; -} - -function setDefaultOptions(newOptions) { - defaultOptions = newOptions; -} - - -/***/ }), - -/***/ 6615: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.formatters = void 0; -var _index = __nccwpck_require__(1412); -var _index2 = __nccwpck_require__(6703); -var _index3 = __nccwpck_require__(7131); -var _index4 = __nccwpck_require__(3080); -var _index5 = __nccwpck_require__(9116); - -var _index6 = __nccwpck_require__(6411); -var _index7 = __nccwpck_require__(8914); - -const dayPeriodEnum = { - am: "am", - pm: "pm", - midnight: "midnight", - noon: "noon", - morning: "morning", - afternoon: "afternoon", - evening: "evening", - night: "night", -}; - -/* - * | | Unit | | Unit | - * |-----|--------------------------------|-----|--------------------------------| - * | a | AM, PM | A* | Milliseconds in day | - * | b | AM, PM, noon, midnight | B | Flexible day period | - * | c | Stand-alone local day of week | C* | Localized hour w/ day period | - * | d | Day of month | D | Day of year | - * | e | Local day of week | E | Day of week | - * | f | | F* | Day of week in month | - * | g* | Modified Julian day | G | Era | - * | h | Hour [1-12] | H | Hour [0-23] | - * | i! | ISO day of week | I! | ISO week of year | - * | j* | Localized hour w/ day period | J* | Localized hour w/o day period | - * | k | Hour [1-24] | K | Hour [0-11] | - * | l* | (deprecated) | L | Stand-alone month | - * | m | Minute | M | Month | - * | n | | N | | - * | o! | Ordinal number modifier | O | Timezone (GMT) | - * | p! | Long localized time | P! | Long localized date | - * | q | Stand-alone quarter | Q | Quarter | - * | r* | Related Gregorian year | R! | ISO week-numbering year | - * | s | Second | S | Fraction of second | - * | t! | Seconds timestamp | T! | Milliseconds timestamp | - * | u | Extended year | U* | Cyclic year | - * | v* | Timezone (generic non-locat.) | V* | Timezone (location) | - * | w | Local week of year | W* | Week of month | - * | x | Timezone (ISO-8601 w/o Z) | X | Timezone (ISO-8601) | - * | y | Year (abs) | Y | Local week-numbering year | - * | z | Timezone (specific non-locat.) | Z* | Timezone (aliases) | - * - * Letters marked by * are not implemented but reserved by Unicode standard. - * - * Letters marked by ! are non-standard, but implemented by date-fns: - * - `o` modifies the previous token to turn it into an ordinal (see `format` docs) - * - `i` is ISO day of week. For `i` and `ii` is returns numeric ISO week days, - * i.e. 7 for Sunday, 1 for Monday, etc. - * - `I` is ISO week of year, as opposed to `w` which is local week of year. - * - `R` is ISO week-numbering year, as opposed to `Y` which is local week-numbering year. - * `R` is supposed to be used in conjunction with `I` and `i` - * for universal ISO week-numbering date, whereas - * `Y` is supposed to be used in conjunction with `w` and `e` - * for week-numbering date specific to the locale. - * - `P` is long localized date format - * - `p` is long localized time format - */ - -const formatters = (exports.formatters = { - // Era - G: function (date, token, localize) { - const era = date.getFullYear() > 0 ? 1 : 0; - switch (token) { - // AD, BC - case "G": - case "GG": - case "GGG": - return localize.era(era, { width: "abbreviated" }); - // A, B - case "GGGGG": - return localize.era(era, { width: "narrow" }); - // Anno Domini, Before Christ - case "GGGG": - default: - return localize.era(era, { width: "wide" }); - } - }, - - // Year - y: function (date, token, localize) { - // Ordinal number - if (token === "yo") { - const signedYear = date.getFullYear(); - // Returns 1 for 1 BC (which is year 0 in JavaScript) - const year = signedYear > 0 ? signedYear : 1 - signedYear; - return localize.ordinalNumber(year, { unit: "year" }); - } - - return _index7.lightFormatters.y(date, token); - }, - - // Local week-numbering year - Y: function (date, token, localize, options) { - const signedWeekYear = (0, _index5.getWeekYear)(date, options); - // Returns 1 for 1 BC (which is year 0 in JavaScript) - const weekYear = signedWeekYear > 0 ? signedWeekYear : 1 - signedWeekYear; - - // Two digit year - if (token === "YY") { - const twoDigitYear = weekYear % 100; - return (0, _index6.addLeadingZeros)(twoDigitYear, 2); - } - - // Ordinal number - if (token === "Yo") { - return localize.ordinalNumber(weekYear, { unit: "year" }); - } - - // Padding - return (0, _index6.addLeadingZeros)(weekYear, token.length); - }, - - // ISO week-numbering year - R: function (date, token) { - const isoWeekYear = (0, _index3.getISOWeekYear)(date); - - // Padding - return (0, _index6.addLeadingZeros)(isoWeekYear, token.length); - }, - - // Extended year. This is a single number designating the year of this calendar system. - // The main difference between `y` and `u` localizers are B.C. years: - // | Year | `y` | `u` | - // |------|-----|-----| - // | AC 1 | 1 | 1 | - // | BC 1 | 1 | 0 | - // | BC 2 | 2 | -1 | - // Also `yy` always returns the last two digits of a year, - // while `uu` pads single digit years to 2 characters and returns other years unchanged. - u: function (date, token) { - const year = date.getFullYear(); - return (0, _index6.addLeadingZeros)(year, token.length); - }, - - // Quarter - Q: function (date, token, localize) { - const quarter = Math.ceil((date.getMonth() + 1) / 3); - switch (token) { - // 1, 2, 3, 4 - case "Q": - return String(quarter); - // 01, 02, 03, 04 - case "QQ": - return (0, _index6.addLeadingZeros)(quarter, 2); - // 1st, 2nd, 3rd, 4th - case "Qo": - return localize.ordinalNumber(quarter, { unit: "quarter" }); - // Q1, Q2, Q3, Q4 - case "QQQ": - return localize.quarter(quarter, { - width: "abbreviated", - context: "formatting", - }); - // 1, 2, 3, 4 (narrow quarter; could be not numerical) - case "QQQQQ": - return localize.quarter(quarter, { - width: "narrow", - context: "formatting", - }); - // 1st quarter, 2nd quarter, ... - case "QQQQ": - default: - return localize.quarter(quarter, { - width: "wide", - context: "formatting", - }); - } - }, - - // Stand-alone quarter - q: function (date, token, localize) { - const quarter = Math.ceil((date.getMonth() + 1) / 3); - switch (token) { - // 1, 2, 3, 4 - case "q": - return String(quarter); - // 01, 02, 03, 04 - case "qq": - return (0, _index6.addLeadingZeros)(quarter, 2); - // 1st, 2nd, 3rd, 4th - case "qo": - return localize.ordinalNumber(quarter, { unit: "quarter" }); - // Q1, Q2, Q3, Q4 - case "qqq": - return localize.quarter(quarter, { - width: "abbreviated", - context: "standalone", - }); - // 1, 2, 3, 4 (narrow quarter; could be not numerical) - case "qqqqq": - return localize.quarter(quarter, { - width: "narrow", - context: "standalone", - }); - // 1st quarter, 2nd quarter, ... - case "qqqq": - default: - return localize.quarter(quarter, { - width: "wide", - context: "standalone", - }); - } - }, - - // Month - M: function (date, token, localize) { - const month = date.getMonth(); - switch (token) { - case "M": - case "MM": - return _index7.lightFormatters.M(date, token); - // 1st, 2nd, ..., 12th - case "Mo": - return localize.ordinalNumber(month + 1, { unit: "month" }); - // Jan, Feb, ..., Dec - case "MMM": - return localize.month(month, { - width: "abbreviated", - context: "formatting", - }); - // J, F, ..., D - case "MMMMM": - return localize.month(month, { - width: "narrow", - context: "formatting", - }); - // January, February, ..., December - case "MMMM": - default: - return localize.month(month, { width: "wide", context: "formatting" }); - } - }, - - // Stand-alone month - L: function (date, token, localize) { - const month = date.getMonth(); - switch (token) { - // 1, 2, ..., 12 - case "L": - return String(month + 1); - // 01, 02, ..., 12 - case "LL": - return (0, _index6.addLeadingZeros)(month + 1, 2); - // 1st, 2nd, ..., 12th - case "Lo": - return localize.ordinalNumber(month + 1, { unit: "month" }); - // Jan, Feb, ..., Dec - case "LLL": - return localize.month(month, { - width: "abbreviated", - context: "standalone", - }); - // J, F, ..., D - case "LLLLL": - return localize.month(month, { - width: "narrow", - context: "standalone", - }); - // January, February, ..., December - case "LLLL": - default: - return localize.month(month, { width: "wide", context: "standalone" }); - } - }, - - // Local week of year - w: function (date, token, localize, options) { - const week = (0, _index4.getWeek)(date, options); - - if (token === "wo") { - return localize.ordinalNumber(week, { unit: "week" }); - } - - return (0, _index6.addLeadingZeros)(week, token.length); - }, - - // ISO week of year - I: function (date, token, localize) { - const isoWeek = (0, _index2.getISOWeek)(date); - - if (token === "Io") { - return localize.ordinalNumber(isoWeek, { unit: "week" }); - } - - return (0, _index6.addLeadingZeros)(isoWeek, token.length); - }, - - // Day of the month - d: function (date, token, localize) { - if (token === "do") { - return localize.ordinalNumber(date.getDate(), { unit: "date" }); - } - - return _index7.lightFormatters.d(date, token); - }, - - // Day of year - D: function (date, token, localize) { - const dayOfYear = (0, _index.getDayOfYear)(date); - - if (token === "Do") { - return localize.ordinalNumber(dayOfYear, { unit: "dayOfYear" }); - } - - return (0, _index6.addLeadingZeros)(dayOfYear, token.length); - }, - - // Day of week - E: function (date, token, localize) { - const dayOfWeek = date.getDay(); - switch (token) { - // Tue - case "E": - case "EE": - case "EEE": - return localize.day(dayOfWeek, { - width: "abbreviated", - context: "formatting", - }); - // T - case "EEEEE": - return localize.day(dayOfWeek, { - width: "narrow", - context: "formatting", - }); - // Tu - case "EEEEEE": - return localize.day(dayOfWeek, { - width: "short", - context: "formatting", - }); - // Tuesday - case "EEEE": - default: - return localize.day(dayOfWeek, { - width: "wide", - context: "formatting", - }); - } - }, - - // Local day of week - e: function (date, token, localize, options) { - const dayOfWeek = date.getDay(); - const localDayOfWeek = (dayOfWeek - options.weekStartsOn + 8) % 7 || 7; - switch (token) { - // Numerical value (Nth day of week with current locale or weekStartsOn) - case "e": - return String(localDayOfWeek); - // Padded numerical value - case "ee": - return (0, _index6.addLeadingZeros)(localDayOfWeek, 2); - // 1st, 2nd, ..., 7th - case "eo": - return localize.ordinalNumber(localDayOfWeek, { unit: "day" }); - case "eee": - return localize.day(dayOfWeek, { - width: "abbreviated", - context: "formatting", - }); - // T - case "eeeee": - return localize.day(dayOfWeek, { - width: "narrow", - context: "formatting", - }); - // Tu - case "eeeeee": - return localize.day(dayOfWeek, { - width: "short", - context: "formatting", - }); - // Tuesday - case "eeee": - default: - return localize.day(dayOfWeek, { - width: "wide", - context: "formatting", - }); - } - }, - - // Stand-alone local day of week - c: function (date, token, localize, options) { - const dayOfWeek = date.getDay(); - const localDayOfWeek = (dayOfWeek - options.weekStartsOn + 8) % 7 || 7; - switch (token) { - // Numerical value (same as in `e`) - case "c": - return String(localDayOfWeek); - // Padded numerical value - case "cc": - return (0, _index6.addLeadingZeros)(localDayOfWeek, token.length); - // 1st, 2nd, ..., 7th - case "co": - return localize.ordinalNumber(localDayOfWeek, { unit: "day" }); - case "ccc": - return localize.day(dayOfWeek, { - width: "abbreviated", - context: "standalone", - }); - // T - case "ccccc": - return localize.day(dayOfWeek, { - width: "narrow", - context: "standalone", - }); - // Tu - case "cccccc": - return localize.day(dayOfWeek, { - width: "short", - context: "standalone", - }); - // Tuesday - case "cccc": - default: - return localize.day(dayOfWeek, { - width: "wide", - context: "standalone", - }); - } - }, - - // ISO day of week - i: function (date, token, localize) { - const dayOfWeek = date.getDay(); - const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; - switch (token) { - // 2 - case "i": - return String(isoDayOfWeek); - // 02 - case "ii": - return (0, _index6.addLeadingZeros)(isoDayOfWeek, token.length); - // 2nd - case "io": - return localize.ordinalNumber(isoDayOfWeek, { unit: "day" }); - // Tue - case "iii": - return localize.day(dayOfWeek, { - width: "abbreviated", - context: "formatting", - }); - // T - case "iiiii": - return localize.day(dayOfWeek, { - width: "narrow", - context: "formatting", - }); - // Tu - case "iiiiii": - return localize.day(dayOfWeek, { - width: "short", - context: "formatting", - }); - // Tuesday - case "iiii": - default: - return localize.day(dayOfWeek, { - width: "wide", - context: "formatting", - }); - } - }, - - // AM or PM - a: function (date, token, localize) { - const hours = date.getHours(); - const dayPeriodEnumValue = hours / 12 >= 1 ? "pm" : "am"; - - switch (token) { - case "a": - case "aa": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "abbreviated", - context: "formatting", - }); - case "aaa": - return localize - .dayPeriod(dayPeriodEnumValue, { - width: "abbreviated", - context: "formatting", - }) - .toLowerCase(); - case "aaaaa": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "narrow", - context: "formatting", - }); - case "aaaa": - default: - return localize.dayPeriod(dayPeriodEnumValue, { - width: "wide", - context: "formatting", - }); - } - }, - - // AM, PM, midnight, noon - b: function (date, token, localize) { - const hours = date.getHours(); - let dayPeriodEnumValue; - if (hours === 12) { - dayPeriodEnumValue = dayPeriodEnum.noon; - } else if (hours === 0) { - dayPeriodEnumValue = dayPeriodEnum.midnight; - } else { - dayPeriodEnumValue = hours / 12 >= 1 ? "pm" : "am"; - } - - switch (token) { - case "b": - case "bb": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "abbreviated", - context: "formatting", - }); - case "bbb": - return localize - .dayPeriod(dayPeriodEnumValue, { - width: "abbreviated", - context: "formatting", - }) - .toLowerCase(); - case "bbbbb": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "narrow", - context: "formatting", - }); - case "bbbb": - default: - return localize.dayPeriod(dayPeriodEnumValue, { - width: "wide", - context: "formatting", - }); - } - }, - - // in the morning, in the afternoon, in the evening, at night - B: function (date, token, localize) { - const hours = date.getHours(); - let dayPeriodEnumValue; - if (hours >= 17) { - dayPeriodEnumValue = dayPeriodEnum.evening; - } else if (hours >= 12) { - dayPeriodEnumValue = dayPeriodEnum.afternoon; - } else if (hours >= 4) { - dayPeriodEnumValue = dayPeriodEnum.morning; - } else { - dayPeriodEnumValue = dayPeriodEnum.night; - } - - switch (token) { - case "B": - case "BB": - case "BBB": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "abbreviated", - context: "formatting", - }); - case "BBBBB": - return localize.dayPeriod(dayPeriodEnumValue, { - width: "narrow", - context: "formatting", - }); - case "BBBB": - default: - return localize.dayPeriod(dayPeriodEnumValue, { - width: "wide", - context: "formatting", - }); - } - }, - - // Hour [1-12] - h: function (date, token, localize) { - if (token === "ho") { - let hours = date.getHours() % 12; - if (hours === 0) hours = 12; - return localize.ordinalNumber(hours, { unit: "hour" }); - } - - return _index7.lightFormatters.h(date, token); - }, - - // Hour [0-23] - H: function (date, token, localize) { - if (token === "Ho") { - return localize.ordinalNumber(date.getHours(), { unit: "hour" }); - } - - return _index7.lightFormatters.H(date, token); - }, - - // Hour [0-11] - K: function (date, token, localize) { - const hours = date.getHours() % 12; - - if (token === "Ko") { - return localize.ordinalNumber(hours, { unit: "hour" }); - } - - return (0, _index6.addLeadingZeros)(hours, token.length); - }, - - // Hour [1-24] - k: function (date, token, localize) { - let hours = date.getHours(); - if (hours === 0) hours = 24; - - if (token === "ko") { - return localize.ordinalNumber(hours, { unit: "hour" }); - } - - return (0, _index6.addLeadingZeros)(hours, token.length); - }, - - // Minute - m: function (date, token, localize) { - if (token === "mo") { - return localize.ordinalNumber(date.getMinutes(), { unit: "minute" }); - } - - return _index7.lightFormatters.m(date, token); - }, - - // Second - s: function (date, token, localize) { - if (token === "so") { - return localize.ordinalNumber(date.getSeconds(), { unit: "second" }); - } - - return _index7.lightFormatters.s(date, token); - }, - - // Fraction of second - S: function (date, token) { - return _index7.lightFormatters.S(date, token); - }, - - // Timezone (ISO-8601. If offset is 0, output is always `'Z'`) - X: function (date, token, _localize) { - const timezoneOffset = date.getTimezoneOffset(); - - if (timezoneOffset === 0) { - return "Z"; - } - - switch (token) { - // Hours and optional minutes - case "X": - return formatTimezoneWithOptionalMinutes(timezoneOffset); - - // Hours, minutes and optional seconds without `:` delimiter - // Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets - // so this token always has the same output as `XX` - case "XXXX": - case "XX": // Hours and minutes without `:` delimiter - return formatTimezone(timezoneOffset); - - // Hours, minutes and optional seconds with `:` delimiter - // Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets - // so this token always has the same output as `XXX` - case "XXXXX": - case "XXX": // Hours and minutes with `:` delimiter - default: - return formatTimezone(timezoneOffset, ":"); - } - }, - - // Timezone (ISO-8601. If offset is 0, output is `'+00:00'` or equivalent) - x: function (date, token, _localize) { - const timezoneOffset = date.getTimezoneOffset(); - - switch (token) { - // Hours and optional minutes - case "x": - return formatTimezoneWithOptionalMinutes(timezoneOffset); - - // Hours, minutes and optional seconds without `:` delimiter - // Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets - // so this token always has the same output as `xx` - case "xxxx": - case "xx": // Hours and minutes without `:` delimiter - return formatTimezone(timezoneOffset); - - // Hours, minutes and optional seconds with `:` delimiter - // Note: neither ISO-8601 nor JavaScript supports seconds in timezone offsets - // so this token always has the same output as `xxx` - case "xxxxx": - case "xxx": // Hours and minutes with `:` delimiter - default: - return formatTimezone(timezoneOffset, ":"); - } - }, - - // Timezone (GMT) - O: function (date, token, _localize) { - const timezoneOffset = date.getTimezoneOffset(); - - switch (token) { - // Short - case "O": - case "OO": - case "OOO": - return "GMT" + formatTimezoneShort(timezoneOffset, ":"); - // Long - case "OOOO": - default: - return "GMT" + formatTimezone(timezoneOffset, ":"); - } - }, - - // Timezone (specific non-location) - z: function (date, token, _localize) { - const timezoneOffset = date.getTimezoneOffset(); - - switch (token) { - // Short - case "z": - case "zz": - case "zzz": - return "GMT" + formatTimezoneShort(timezoneOffset, ":"); - // Long - case "zzzz": - default: - return "GMT" + formatTimezone(timezoneOffset, ":"); - } - }, - - // Seconds timestamp - t: function (date, token, _localize) { - const timestamp = Math.trunc(+date / 1000); - return (0, _index6.addLeadingZeros)(timestamp, token.length); - }, - - // Milliseconds timestamp - T: function (date, token, _localize) { - return (0, _index6.addLeadingZeros)(+date, token.length); - }, -}); - -function formatTimezoneShort(offset, delimiter = "") { - const sign = offset > 0 ? "-" : "+"; - const absOffset = Math.abs(offset); - const hours = Math.trunc(absOffset / 60); - const minutes = absOffset % 60; - if (minutes === 0) { - return sign + String(hours); - } - return ( - sign + String(hours) + delimiter + (0, _index6.addLeadingZeros)(minutes, 2) - ); -} - -function formatTimezoneWithOptionalMinutes(offset, delimiter) { - if (offset % 60 === 0) { - const sign = offset > 0 ? "-" : "+"; - return sign + (0, _index6.addLeadingZeros)(Math.abs(offset) / 60, 2); - } - return formatTimezone(offset, delimiter); -} - -function formatTimezone(offset, delimiter = "") { - const sign = offset > 0 ? "-" : "+"; - const absOffset = Math.abs(offset); - const hours = (0, _index6.addLeadingZeros)(Math.trunc(absOffset / 60), 2); - const minutes = (0, _index6.addLeadingZeros)(absOffset % 60, 2); - return sign + hours + delimiter + minutes; -} - - -/***/ }), - -/***/ 8914: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.lightFormatters = void 0; -var _index = __nccwpck_require__(6411); - -/* - * | | Unit | | Unit | - * |-----|--------------------------------|-----|--------------------------------| - * | a | AM, PM | A* | | - * | d | Day of month | D | | - * | h | Hour [1-12] | H | Hour [0-23] | - * | m | Minute | M | Month | - * | s | Second | S | Fraction of second | - * | y | Year (abs) | Y | | - * - * Letters marked by * are not implemented but reserved by Unicode standard. - */ - -const lightFormatters = (exports.lightFormatters = { - // Year - y(date, token) { - // From http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_tokens - // | Year | y | yy | yyy | yyyy | yyyyy | - // |----------|-------|----|-------|-------|-------| - // | AD 1 | 1 | 01 | 001 | 0001 | 00001 | - // | AD 12 | 12 | 12 | 012 | 0012 | 00012 | - // | AD 123 | 123 | 23 | 123 | 0123 | 00123 | - // | AD 1234 | 1234 | 34 | 1234 | 1234 | 01234 | - // | AD 12345 | 12345 | 45 | 12345 | 12345 | 12345 | - - const signedYear = date.getFullYear(); - // Returns 1 for 1 BC (which is year 0 in JavaScript) - const year = signedYear > 0 ? signedYear : 1 - signedYear; - return (0, _index.addLeadingZeros)( - token === "yy" ? year % 100 : year, - token.length, - ); - }, - - // Month - M(date, token) { - const month = date.getMonth(); - return token === "M" - ? String(month + 1) - : (0, _index.addLeadingZeros)(month + 1, 2); - }, - - // Day of the month - d(date, token) { - return (0, _index.addLeadingZeros)(date.getDate(), token.length); - }, - - // AM or PM - a(date, token) { - const dayPeriodEnumValue = date.getHours() / 12 >= 1 ? "pm" : "am"; - - switch (token) { - case "a": - case "aa": - return dayPeriodEnumValue.toUpperCase(); - case "aaa": - return dayPeriodEnumValue; - case "aaaaa": - return dayPeriodEnumValue[0]; - case "aaaa": - default: - return dayPeriodEnumValue === "am" ? "a.m." : "p.m."; - } - }, - - // Hour [1-12] - h(date, token) { - return (0, _index.addLeadingZeros)( - date.getHours() % 12 || 12, - token.length, - ); - }, - - // Hour [0-23] - H(date, token) { - return (0, _index.addLeadingZeros)(date.getHours(), token.length); - }, - - // Minute - m(date, token) { - return (0, _index.addLeadingZeros)(date.getMinutes(), token.length); - }, - - // Second - s(date, token) { - return (0, _index.addLeadingZeros)(date.getSeconds(), token.length); - }, - - // Fraction of second - S(date, token) { - const numberOfDigits = token.length; - const milliseconds = date.getMilliseconds(); - const fractionalSeconds = Math.trunc( - milliseconds * Math.pow(10, numberOfDigits - 3), - ); - return (0, _index.addLeadingZeros)(fractionalSeconds, token.length); - }, -}); - - -/***/ }), - -/***/ 6376: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.longFormatters = void 0; - -const dateLongFormatter = (pattern, formatLong) => { - switch (pattern) { - case "P": - return formatLong.date({ width: "short" }); - case "PP": - return formatLong.date({ width: "medium" }); - case "PPP": - return formatLong.date({ width: "long" }); - case "PPPP": - default: - return formatLong.date({ width: "full" }); - } -}; - -const timeLongFormatter = (pattern, formatLong) => { - switch (pattern) { - case "p": - return formatLong.time({ width: "short" }); - case "pp": - return formatLong.time({ width: "medium" }); - case "ppp": - return formatLong.time({ width: "long" }); - case "pppp": - default: - return formatLong.time({ width: "full" }); - } -}; - -const dateTimeLongFormatter = (pattern, formatLong) => { - const matchResult = pattern.match(/(P+)(p+)?/) || []; - const datePattern = matchResult[1]; - const timePattern = matchResult[2]; - - if (!timePattern) { - return dateLongFormatter(pattern, formatLong); - } - - let dateTimeFormat; - - switch (datePattern) { - case "P": - dateTimeFormat = formatLong.dateTime({ width: "short" }); - break; - case "PP": - dateTimeFormat = formatLong.dateTime({ width: "medium" }); - break; - case "PPP": - dateTimeFormat = formatLong.dateTime({ width: "long" }); - break; - case "PPPP": - default: - dateTimeFormat = formatLong.dateTime({ width: "full" }); - break; - } - - return dateTimeFormat - .replace("{{date}}", dateLongFormatter(datePattern, formatLong)) - .replace("{{time}}", timeLongFormatter(timePattern, formatLong)); -}; - -const longFormatters = (exports.longFormatters = { - p: timeLongFormatter, - P: dateTimeLongFormatter, -}); - - -/***/ }), - -/***/ 1546: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getTimezoneOffsetInMilliseconds = getTimezoneOffsetInMilliseconds; -var _index = __nccwpck_require__(6439); - -/** - * Google Chrome as of 67.0.3396.87 introduced timezones with offset that includes seconds. - * They usually appear for dates that denote time before the timezones were introduced - * (e.g. for 'Europe/Prague' timezone the offset is GMT+00:57:44 before 1 October 1891 - * and GMT+01:00:00 after that date) - * - * Date#getTimezoneOffset returns the offset in minutes and would return 57 for the example above, - * which would lead to incorrect calculations. - * - * This function returns the timezone offset in milliseconds that takes seconds in account. - */ -function getTimezoneOffsetInMilliseconds(date) { - const _date = (0, _index.toDate)(date); - const utcDate = new Date( - Date.UTC( - _date.getFullYear(), - _date.getMonth(), - _date.getDate(), - _date.getHours(), - _date.getMinutes(), - _date.getSeconds(), - _date.getMilliseconds(), - ), - ); - utcDate.setUTCFullYear(_date.getFullYear()); - return +date - +utcDate; -} - - -/***/ }), - -/***/ 8897: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.normalizeDates = normalizeDates; -var _index = __nccwpck_require__(926); - -function normalizeDates(context, ...dates) { - const normalize = _index.constructFrom.bind( - null, - context || dates.find((date) => typeof date === "object"), - ); - return dates.map(normalize); -} - - -/***/ }), - -/***/ 3554: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.isProtectedDayOfYearToken = isProtectedDayOfYearToken; -exports.isProtectedWeekYearToken = isProtectedWeekYearToken; -exports.warnOrThrowProtectedError = warnOrThrowProtectedError; -const dayOfYearTokenRE = /^D+$/; -const weekYearTokenRE = /^Y+$/; - -const throwTokens = ["D", "DD", "YY", "YYYY"]; - -function isProtectedDayOfYearToken(token) { - return dayOfYearTokenRE.test(token); -} - -function isProtectedWeekYearToken(token) { - return weekYearTokenRE.test(token); -} - -function warnOrThrowProtectedError(token, format, input) { - const _message = message(token, format, input); - console.warn(_message); - if (throwTokens.includes(token)) throw new RangeError(_message); -} - -function message(token, format, input) { - const subject = token[0] === "Y" ? "years" : "days of the month"; - return `Use \`${token.toLowerCase()}\` instead of \`${token}\` (in \`${format}\`) for formatting ${subject} to the input \`${input}\`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md`; -} - - -/***/ }), - -/***/ 4278: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.secondsInYear = - exports.secondsInWeek = - exports.secondsInQuarter = - exports.secondsInMonth = - exports.secondsInMinute = - exports.secondsInHour = - exports.secondsInDay = - exports.quartersInYear = - exports.monthsInYear = - exports.monthsInQuarter = - exports.minutesInYear = - exports.minutesInMonth = - exports.minutesInHour = - exports.minutesInDay = - exports.minTime = - exports.millisecondsInWeek = - exports.millisecondsInSecond = - exports.millisecondsInMinute = - exports.millisecondsInHour = - exports.millisecondsInDay = - exports.maxTime = - exports.daysInYear = - exports.daysInWeek = - exports.constructFromSymbol = - void 0; /** - * @module constants - * @summary Useful constants - * @description - * Collection of useful date constants. - * - * The constants could be imported from `date-fns/constants`: - * - * ```ts - * import { maxTime, minTime } from "date-fns/constants"; - * - * function isAllowedTime(time) { - * return time <= maxTime && time >= minTime; - * } - * ``` - */ - -/** - * @constant - * @name daysInWeek - * @summary Days in 1 week. - */ -const daysInWeek = (exports.daysInWeek = 7); - -/** - * @constant - * @name daysInYear - * @summary Days in 1 year. - * - * @description - * How many days in a year. - * - * One years equals 365.2425 days according to the formula: - * - * > Leap year occurs every 4 years, except for years that are divisible by 100 and not divisible by 400. - * > 1 mean year = (365+1/4-1/100+1/400) days = 365.2425 days - */ -const daysInYear = (exports.daysInYear = 365.2425); - -/** - * @constant - * @name maxTime - * @summary Maximum allowed time. - * - * @example - * import { maxTime } from "date-fns/constants"; - * - * const isValid = 8640000000000001 <= maxTime; - * //=> false - * - * new Date(8640000000000001); - * //=> Invalid Date - */ -const maxTime = (exports.maxTime = Math.pow(10, 8) * 24 * 60 * 60 * 1000); - -/** - * @constant - * @name minTime - * @summary Minimum allowed time. - * - * @example - * import { minTime } from "date-fns/constants"; - * - * const isValid = -8640000000000001 >= minTime; - * //=> false - * - * new Date(-8640000000000001) - * //=> Invalid Date - */ -const minTime = (exports.minTime = -maxTime); - -/** - * @constant - * @name millisecondsInWeek - * @summary Milliseconds in 1 week. - */ -const millisecondsInWeek = (exports.millisecondsInWeek = 604800000); - -/** - * @constant - * @name millisecondsInDay - * @summary Milliseconds in 1 day. - */ -const millisecondsInDay = (exports.millisecondsInDay = 86400000); - -/** - * @constant - * @name millisecondsInMinute - * @summary Milliseconds in 1 minute - */ -const millisecondsInMinute = (exports.millisecondsInMinute = 60000); - -/** - * @constant - * @name millisecondsInHour - * @summary Milliseconds in 1 hour - */ -const millisecondsInHour = (exports.millisecondsInHour = 3600000); - -/** - * @constant - * @name millisecondsInSecond - * @summary Milliseconds in 1 second - */ -const millisecondsInSecond = (exports.millisecondsInSecond = 1000); - -/** - * @constant - * @name minutesInYear - * @summary Minutes in 1 year. - */ -const minutesInYear = (exports.minutesInYear = 525600); - -/** - * @constant - * @name minutesInMonth - * @summary Minutes in 1 month. - */ -const minutesInMonth = (exports.minutesInMonth = 43200); - -/** - * @constant - * @name minutesInDay - * @summary Minutes in 1 day. - */ -const minutesInDay = (exports.minutesInDay = 1440); - -/** - * @constant - * @name minutesInHour - * @summary Minutes in 1 hour. - */ -const minutesInHour = (exports.minutesInHour = 60); - -/** - * @constant - * @name monthsInQuarter - * @summary Months in 1 quarter. - */ -const monthsInQuarter = (exports.monthsInQuarter = 3); - -/** - * @constant - * @name monthsInYear - * @summary Months in 1 year. - */ -const monthsInYear = (exports.monthsInYear = 12); - -/** - * @constant - * @name quartersInYear - * @summary Quarters in 1 year - */ -const quartersInYear = (exports.quartersInYear = 4); - -/** - * @constant - * @name secondsInHour - * @summary Seconds in 1 hour. - */ -const secondsInHour = (exports.secondsInHour = 3600); - -/** - * @constant - * @name secondsInMinute - * @summary Seconds in 1 minute. - */ -const secondsInMinute = (exports.secondsInMinute = 60); - -/** - * @constant - * @name secondsInDay - * @summary Seconds in 1 day. - */ -const secondsInDay = (exports.secondsInDay = secondsInHour * 24); - -/** - * @constant - * @name secondsInWeek - * @summary Seconds in 1 week. - */ -const secondsInWeek = (exports.secondsInWeek = secondsInDay * 7); - -/** - * @constant - * @name secondsInYear - * @summary Seconds in 1 year. - */ -const secondsInYear = (exports.secondsInYear = secondsInDay * daysInYear); - -/** - * @constant - * @name secondsInMonth - * @summary Seconds in 1 month - */ -const secondsInMonth = (exports.secondsInMonth = secondsInYear / 12); - -/** - * @constant - * @name secondsInQuarter - * @summary Seconds in 1 quarter. - */ -const secondsInQuarter = (exports.secondsInQuarter = secondsInMonth * 3); - -/** - * @constant - * @name constructFromSymbol - * @summary Symbol enabling Date extensions to inherit properties from the reference date. - * - * The symbol is used to enable the `constructFrom` function to construct a date - * using a reference date and a value. It allows to transfer extra properties - * from the reference date to the new date. It's useful for extensions like - * [`TZDate`](https://github.com/date-fns/tz) that accept a time zone as - * a constructor argument. - */ -const constructFromSymbol = (exports.constructFromSymbol = - Symbol.for("constructDateFrom")); - - -/***/ }), - -/***/ 926: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.constructFrom = constructFrom; -var _index = __nccwpck_require__(4278); - -/** - * @name constructFrom - * @category Generic Helpers - * @summary Constructs a date using the reference date and the value - * - * @description - * The function constructs a new date using the constructor from the reference - * date and the given value. It helps to build generic functions that accept - * date extensions. - * - * It defaults to `Date` if the passed reference date is a number or a string. - * - * Starting from v3.7.0, it allows to construct a date using `[Symbol.for("constructDateFrom")]` - * enabling to transfer extra properties from the reference date to the new date. - * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz) - * that accept a time zone as a constructor argument. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * - * @param date - The reference date to take constructor from - * @param value - The value to create the date - * - * @returns Date initialized using the given date and value - * - * @example - * import { constructFrom } from "date-fns"; - * - * // A function that clones a date preserving the original type - * function cloneDate(date: DateType): DateType { - * return constructFrom( - * date, // Use constructor from the given date - * date.getTime() // Use the date value to create a new date - * ); - * } - */ -function constructFrom(date, value) { - if (typeof date === "function") return date(value); - - if (date && typeof date === "object" && _index.constructFromSymbol in date) - return date[_index.constructFromSymbol](value); - - if (date instanceof Date) return new date.constructor(value); - - return new Date(value); -} - - -/***/ }), - -/***/ 5671: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.differenceInCalendarDays = differenceInCalendarDays; -var _index = __nccwpck_require__(1546); -var _index2 = __nccwpck_require__(8897); -var _index3 = __nccwpck_require__(4278); -var _index4 = __nccwpck_require__(5951); - -/** - * The {@link differenceInCalendarDays} function options. - */ - -/** - * @name differenceInCalendarDays - * @category Day Helpers - * @summary Get the number of calendar days between the given dates. - * - * @description - * Get the number of calendar days between the given dates. This means that the times are removed - * from the dates and then the difference in days is calculated. - * - * @param laterDate - The later date - * @param earlierDate - The earlier date - * @param options - The options object - * - * @returns The number of calendar days - * - * @example - * // How many calendar days are between - * // 2 July 2011 23:00:00 and 2 July 2012 00:00:00? - * const result = differenceInCalendarDays( - * new Date(2012, 6, 2, 0, 0), - * new Date(2011, 6, 2, 23, 0) - * ) - * //=> 366 - * // How many calendar days are between - * // 2 July 2011 23:59:00 and 3 July 2011 00:01:00? - * const result = differenceInCalendarDays( - * new Date(2011, 6, 3, 0, 1), - * new Date(2011, 6, 2, 23, 59) - * ) - * //=> 1 - */ -function differenceInCalendarDays(laterDate, earlierDate, options) { - const [laterDate_, earlierDate_] = (0, _index2.normalizeDates)( - options?.in, - laterDate, - earlierDate, - ); - - const laterStartOfDay = (0, _index4.startOfDay)(laterDate_); - const earlierStartOfDay = (0, _index4.startOfDay)(earlierDate_); - - const laterTimestamp = - +laterStartOfDay - - (0, _index.getTimezoneOffsetInMilliseconds)(laterStartOfDay); - const earlierTimestamp = - +earlierStartOfDay - - (0, _index.getTimezoneOffsetInMilliseconds)(earlierStartOfDay); - - // Round the number of days to the nearest integer because the number of - // milliseconds in a day is not constant (e.g. it's different in the week of - // the daylight saving time clock shift). - return Math.round( - (laterTimestamp - earlierTimestamp) / _index3.millisecondsInDay, - ); -} - - -/***/ }), - -/***/ 2464: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.format = exports.formatDate = format; -Object.defineProperty(exports, "formatters", ({ - enumerable: true, - get: function () { - return _index3.formatters; - }, -})); -Object.defineProperty(exports, "longFormatters", ({ - enumerable: true, - get: function () { - return _index4.longFormatters; - }, -})); -var _index = __nccwpck_require__(7479); -var _index2 = __nccwpck_require__(5586); -var _index3 = __nccwpck_require__(6615); -var _index4 = __nccwpck_require__(6376); -var _index5 = __nccwpck_require__(3554); - -var _index6 = __nccwpck_require__(6142); -var _index7 = __nccwpck_require__(6439); - -// Rexports of internal for libraries to use. -// See: https://github.com/date-fns/date-fns/issues/3638#issuecomment-1877082874 - -// This RegExp consists of three parts separated by `|`: -// - [yYQqMLwIdDecihHKkms]o matches any available ordinal number token -// (one of the certain letters followed by `o`) -// - (\w)\1* matches any sequences of the same letter -// - '' matches two quote characters in a row -// - '(''|[^'])+('|$) matches anything surrounded by two quote characters ('), -// except a single quote symbol, which ends the sequence. -// Two quote characters do not end the sequence. -// If there is no matching single quote -// then the sequence will continue until the end of the string. -// - . matches any single character unmatched by previous parts of the RegExps -const formattingTokensRegExp = - /[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g; - -// This RegExp catches symbols escaped by quotes, and also -// sequences of symbols P, p, and the combinations like `PPPPPPPppppp` -const longFormattingTokensRegExp = /P+p+|P+|p+|''|'(''|[^'])+('|$)|./g; - -const escapedStringRegExp = /^'([^]*?)'?$/; -const doubleQuoteRegExp = /''/g; -const unescapedLatinCharacterRegExp = /[a-zA-Z]/; - -/** - * The {@link format} function options. - */ - -/** - * @name format - * @alias formatDate - * @category Common Helpers - * @summary Format the date. - * - * @description - * Return the formatted date string in the given format. The result may vary by locale. - * - * > ⚠️ Please note that the `format` tokens differ from Moment.js and other libraries. - * > See: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * - * The characters wrapped between two single quotes characters (') are escaped. - * Two single quotes in a row, whether inside or outside a quoted sequence, represent a 'real' single quote. - * (see the last example) - * - * Format of the string is based on Unicode Technical Standard #35: - * https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table - * with a few additions (see note 7 below the table). - * - * Accepted patterns: - * | Unit | Pattern | Result examples | Notes | - * |---------------------------------|---------|-----------------------------------|-------| - * | Era | G..GGG | AD, BC | | - * | | GGGG | Anno Domini, Before Christ | 2 | - * | | GGGGG | A, B | | - * | Calendar year | y | 44, 1, 1900, 2017 | 5 | - * | | yo | 44th, 1st, 0th, 17th | 5,7 | - * | | yy | 44, 01, 00, 17 | 5 | - * | | yyy | 044, 001, 1900, 2017 | 5 | - * | | yyyy | 0044, 0001, 1900, 2017 | 5 | - * | | yyyyy | ... | 3,5 | - * | Local week-numbering year | Y | 44, 1, 1900, 2017 | 5 | - * | | Yo | 44th, 1st, 1900th, 2017th | 5,7 | - * | | YY | 44, 01, 00, 17 | 5,8 | - * | | YYY | 044, 001, 1900, 2017 | 5 | - * | | YYYY | 0044, 0001, 1900, 2017 | 5,8 | - * | | YYYYY | ... | 3,5 | - * | ISO week-numbering year | R | -43, 0, 1, 1900, 2017 | 5,7 | - * | | RR | -43, 00, 01, 1900, 2017 | 5,7 | - * | | RRR | -043, 000, 001, 1900, 2017 | 5,7 | - * | | RRRR | -0043, 0000, 0001, 1900, 2017 | 5,7 | - * | | RRRRR | ... | 3,5,7 | - * | Extended year | u | -43, 0, 1, 1900, 2017 | 5 | - * | | uu | -43, 01, 1900, 2017 | 5 | - * | | uuu | -043, 001, 1900, 2017 | 5 | - * | | uuuu | -0043, 0001, 1900, 2017 | 5 | - * | | uuuuu | ... | 3,5 | - * | Quarter (formatting) | Q | 1, 2, 3, 4 | | - * | | Qo | 1st, 2nd, 3rd, 4th | 7 | - * | | QQ | 01, 02, 03, 04 | | - * | | QQQ | Q1, Q2, Q3, Q4 | | - * | | QQQQ | 1st quarter, 2nd quarter, ... | 2 | - * | | QQQQQ | 1, 2, 3, 4 | 4 | - * | Quarter (stand-alone) | q | 1, 2, 3, 4 | | - * | | qo | 1st, 2nd, 3rd, 4th | 7 | - * | | qq | 01, 02, 03, 04 | | - * | | qqq | Q1, Q2, Q3, Q4 | | - * | | qqqq | 1st quarter, 2nd quarter, ... | 2 | - * | | qqqqq | 1, 2, 3, 4 | 4 | - * | Month (formatting) | M | 1, 2, ..., 12 | | - * | | Mo | 1st, 2nd, ..., 12th | 7 | - * | | MM | 01, 02, ..., 12 | | - * | | MMM | Jan, Feb, ..., Dec | | - * | | MMMM | January, February, ..., December | 2 | - * | | MMMMM | J, F, ..., D | | - * | Month (stand-alone) | L | 1, 2, ..., 12 | | - * | | Lo | 1st, 2nd, ..., 12th | 7 | - * | | LL | 01, 02, ..., 12 | | - * | | LLL | Jan, Feb, ..., Dec | | - * | | LLLL | January, February, ..., December | 2 | - * | | LLLLL | J, F, ..., D | | - * | Local week of year | w | 1, 2, ..., 53 | | - * | | wo | 1st, 2nd, ..., 53th | 7 | - * | | ww | 01, 02, ..., 53 | | - * | ISO week of year | I | 1, 2, ..., 53 | 7 | - * | | Io | 1st, 2nd, ..., 53th | 7 | - * | | II | 01, 02, ..., 53 | 7 | - * | Day of month | d | 1, 2, ..., 31 | | - * | | do | 1st, 2nd, ..., 31st | 7 | - * | | dd | 01, 02, ..., 31 | | - * | Day of year | D | 1, 2, ..., 365, 366 | 9 | - * | | Do | 1st, 2nd, ..., 365th, 366th | 7 | - * | | DD | 01, 02, ..., 365, 366 | 9 | - * | | DDD | 001, 002, ..., 365, 366 | | - * | | DDDD | ... | 3 | - * | Day of week (formatting) | E..EEE | Mon, Tue, Wed, ..., Sun | | - * | | EEEE | Monday, Tuesday, ..., Sunday | 2 | - * | | EEEEE | M, T, W, T, F, S, S | | - * | | EEEEEE | Mo, Tu, We, Th, Fr, Sa, Su | | - * | ISO day of week (formatting) | i | 1, 2, 3, ..., 7 | 7 | - * | | io | 1st, 2nd, ..., 7th | 7 | - * | | ii | 01, 02, ..., 07 | 7 | - * | | iii | Mon, Tue, Wed, ..., Sun | 7 | - * | | iiii | Monday, Tuesday, ..., Sunday | 2,7 | - * | | iiiii | M, T, W, T, F, S, S | 7 | - * | | iiiiii | Mo, Tu, We, Th, Fr, Sa, Su | 7 | - * | Local day of week (formatting) | e | 2, 3, 4, ..., 1 | | - * | | eo | 2nd, 3rd, ..., 1st | 7 | - * | | ee | 02, 03, ..., 01 | | - * | | eee | Mon, Tue, Wed, ..., Sun | | - * | | eeee | Monday, Tuesday, ..., Sunday | 2 | - * | | eeeee | M, T, W, T, F, S, S | | - * | | eeeeee | Mo, Tu, We, Th, Fr, Sa, Su | | - * | Local day of week (stand-alone) | c | 2, 3, 4, ..., 1 | | - * | | co | 2nd, 3rd, ..., 1st | 7 | - * | | cc | 02, 03, ..., 01 | | - * | | ccc | Mon, Tue, Wed, ..., Sun | | - * | | cccc | Monday, Tuesday, ..., Sunday | 2 | - * | | ccccc | M, T, W, T, F, S, S | | - * | | cccccc | Mo, Tu, We, Th, Fr, Sa, Su | | - * | AM, PM | a..aa | AM, PM | | - * | | aaa | am, pm | | - * | | aaaa | a.m., p.m. | 2 | - * | | aaaaa | a, p | | - * | AM, PM, noon, midnight | b..bb | AM, PM, noon, midnight | | - * | | bbb | am, pm, noon, midnight | | - * | | bbbb | a.m., p.m., noon, midnight | 2 | - * | | bbbbb | a, p, n, mi | | - * | Flexible day period | B..BBB | at night, in the morning, ... | | - * | | BBBB | at night, in the morning, ... | 2 | - * | | BBBBB | at night, in the morning, ... | | - * | Hour [1-12] | h | 1, 2, ..., 11, 12 | | - * | | ho | 1st, 2nd, ..., 11th, 12th | 7 | - * | | hh | 01, 02, ..., 11, 12 | | - * | Hour [0-23] | H | 0, 1, 2, ..., 23 | | - * | | Ho | 0th, 1st, 2nd, ..., 23rd | 7 | - * | | HH | 00, 01, 02, ..., 23 | | - * | Hour [0-11] | K | 1, 2, ..., 11, 0 | | - * | | Ko | 1st, 2nd, ..., 11th, 0th | 7 | - * | | KK | 01, 02, ..., 11, 00 | | - * | Hour [1-24] | k | 24, 1, 2, ..., 23 | | - * | | ko | 24th, 1st, 2nd, ..., 23rd | 7 | - * | | kk | 24, 01, 02, ..., 23 | | - * | Minute | m | 0, 1, ..., 59 | | - * | | mo | 0th, 1st, ..., 59th | 7 | - * | | mm | 00, 01, ..., 59 | | - * | Second | s | 0, 1, ..., 59 | | - * | | so | 0th, 1st, ..., 59th | 7 | - * | | ss | 00, 01, ..., 59 | | - * | Fraction of second | S | 0, 1, ..., 9 | | - * | | SS | 00, 01, ..., 99 | | - * | | SSS | 000, 001, ..., 999 | | - * | | SSSS | ... | 3 | - * | Timezone (ISO-8601 w/ Z) | X | -08, +0530, Z | | - * | | XX | -0800, +0530, Z | | - * | | XXX | -08:00, +05:30, Z | | - * | | XXXX | -0800, +0530, Z, +123456 | 2 | - * | | XXXXX | -08:00, +05:30, Z, +12:34:56 | | - * | Timezone (ISO-8601 w/o Z) | x | -08, +0530, +00 | | - * | | xx | -0800, +0530, +0000 | | - * | | xxx | -08:00, +05:30, +00:00 | 2 | - * | | xxxx | -0800, +0530, +0000, +123456 | | - * | | xxxxx | -08:00, +05:30, +00:00, +12:34:56 | | - * | Timezone (GMT) | O...OOO | GMT-8, GMT+5:30, GMT+0 | | - * | | OOOO | GMT-08:00, GMT+05:30, GMT+00:00 | 2 | - * | Timezone (specific non-locat.) | z...zzz | GMT-8, GMT+5:30, GMT+0 | 6 | - * | | zzzz | GMT-08:00, GMT+05:30, GMT+00:00 | 2,6 | - * | Seconds timestamp | t | 512969520 | 7 | - * | | tt | ... | 3,7 | - * | Milliseconds timestamp | T | 512969520900 | 7 | - * | | TT | ... | 3,7 | - * | Long localized date | P | 04/29/1453 | 7 | - * | | PP | Apr 29, 1453 | 7 | - * | | PPP | April 29th, 1453 | 7 | - * | | PPPP | Friday, April 29th, 1453 | 2,7 | - * | Long localized time | p | 12:00 AM | 7 | - * | | pp | 12:00:00 AM | 7 | - * | | ppp | 12:00:00 AM GMT+2 | 7 | - * | | pppp | 12:00:00 AM GMT+02:00 | 2,7 | - * | Combination of date and time | Pp | 04/29/1453, 12:00 AM | 7 | - * | | PPpp | Apr 29, 1453, 12:00:00 AM | 7 | - * | | PPPppp | April 29th, 1453 at ... | 7 | - * | | PPPPpppp| Friday, April 29th, 1453 at ... | 2,7 | - * Notes: - * 1. "Formatting" units (e.g. formatting quarter) in the default en-US locale - * are the same as "stand-alone" units, but are different in some languages. - * "Formatting" units are declined according to the rules of the language - * in the context of a date. "Stand-alone" units are always nominative singular: - * - * `format(new Date(2017, 10, 6), 'do LLLL', {locale: cs}) //=> '6. listopad'` - * - * `format(new Date(2017, 10, 6), 'do MMMM', {locale: cs}) //=> '6. listopadu'` - * - * 2. Any sequence of the identical letters is a pattern, unless it is escaped by - * the single quote characters (see below). - * If the sequence is longer than listed in table (e.g. `EEEEEEEEEEE`) - * the output will be the same as default pattern for this unit, usually - * the longest one (in case of ISO weekdays, `EEEE`). Default patterns for units - * are marked with "2" in the last column of the table. - * - * `format(new Date(2017, 10, 6), 'MMM') //=> 'Nov'` - * - * `format(new Date(2017, 10, 6), 'MMMM') //=> 'November'` - * - * `format(new Date(2017, 10, 6), 'MMMMM') //=> 'N'` - * - * `format(new Date(2017, 10, 6), 'MMMMMM') //=> 'November'` - * - * `format(new Date(2017, 10, 6), 'MMMMMMM') //=> 'November'` - * - * 3. Some patterns could be unlimited length (such as `yyyyyyyy`). - * The output will be padded with zeros to match the length of the pattern. - * - * `format(new Date(2017, 10, 6), 'yyyyyyyy') //=> '00002017'` - * - * 4. `QQQQQ` and `qqqqq` could be not strictly numerical in some locales. - * These tokens represent the shortest form of the quarter. - * - * 5. The main difference between `y` and `u` patterns are B.C. years: - * - * | Year | `y` | `u` | - * |------|-----|-----| - * | AC 1 | 1 | 1 | - * | BC 1 | 1 | 0 | - * | BC 2 | 2 | -1 | - * - * Also `yy` always returns the last two digits of a year, - * while `uu` pads single digit years to 2 characters and returns other years unchanged: - * - * | Year | `yy` | `uu` | - * |------|------|------| - * | 1 | 01 | 01 | - * | 14 | 14 | 14 | - * | 376 | 76 | 376 | - * | 1453 | 53 | 1453 | - * - * The same difference is true for local and ISO week-numbering years (`Y` and `R`), - * except local week-numbering years are dependent on `options.weekStartsOn` - * and `options.firstWeekContainsDate` (compare [getISOWeekYear](https://date-fns.org/docs/getISOWeekYear) - * and [getWeekYear](https://date-fns.org/docs/getWeekYear)). - * - * 6. Specific non-location timezones are currently unavailable in `date-fns`, - * so right now these tokens fall back to GMT timezones. - * - * 7. These patterns are not in the Unicode Technical Standard #35: - * - `i`: ISO day of week - * - `I`: ISO week of year - * - `R`: ISO week-numbering year - * - `t`: seconds timestamp - * - `T`: milliseconds timestamp - * - `o`: ordinal number modifier - * - `P`: long localized date - * - `p`: long localized time - * - * 8. `YY` and `YYYY` tokens represent week-numbering years but they are often confused with years. - * You should enable `options.useAdditionalWeekYearTokens` to use them. See: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * - * 9. `D` and `DD` tokens represent days of the year but they are often confused with days of the month. - * You should enable `options.useAdditionalDayOfYearTokens` to use them. See: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * - * @param date - The original date - * @param format - The string of tokens - * @param options - An object with options - * - * @returns The formatted date string - * - * @throws `date` must not be Invalid Date - * @throws `options.locale` must contain `localize` property - * @throws `options.locale` must contain `formatLong` property - * @throws use `yyyy` instead of `YYYY` for formatting years using [format provided] to the input [input provided]; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * @throws use `yy` instead of `YY` for formatting years using [format provided] to the input [input provided]; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * @throws use `d` instead of `D` for formatting days of the month using [format provided] to the input [input provided]; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * @throws use `dd` instead of `DD` for formatting days of the month using [format provided] to the input [input provided]; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md - * @throws format string contains an unescaped latin alphabet character - * - * @example - * // Represent 11 February 2014 in middle-endian format: - * const result = format(new Date(2014, 1, 11), 'MM/dd/yyyy') - * //=> '02/11/2014' - * - * @example - * // Represent 2 July 2014 in Esperanto: - * import { eoLocale } from 'date-fns/locale/eo' - * const result = format(new Date(2014, 6, 2), "do 'de' MMMM yyyy", { - * locale: eoLocale - * }) - * //=> '2-a de julio 2014' - * - * @example - * // Escape string by single quote characters: - * const result = format(new Date(2014, 6, 2, 15), "h 'o''clock'") - * //=> "3 o'clock" - */ -function format(date, formatStr, options) { - const defaultOptions = (0, _index2.getDefaultOptions)(); - const locale = - options?.locale ?? defaultOptions.locale ?? _index.defaultLocale; - - const firstWeekContainsDate = - options?.firstWeekContainsDate ?? - options?.locale?.options?.firstWeekContainsDate ?? - defaultOptions.firstWeekContainsDate ?? - defaultOptions.locale?.options?.firstWeekContainsDate ?? - 1; - - const weekStartsOn = - options?.weekStartsOn ?? - options?.locale?.options?.weekStartsOn ?? - defaultOptions.weekStartsOn ?? - defaultOptions.locale?.options?.weekStartsOn ?? - 0; - - const originalDate = (0, _index7.toDate)(date, options?.in); - - if (!(0, _index6.isValid)(originalDate)) { - throw new RangeError("Invalid time value"); - } - - let parts = formatStr - .match(longFormattingTokensRegExp) - .map((substring) => { - const firstCharacter = substring[0]; - if (firstCharacter === "p" || firstCharacter === "P") { - const longFormatter = _index4.longFormatters[firstCharacter]; - return longFormatter(substring, locale.formatLong); - } - return substring; - }) - .join("") - .match(formattingTokensRegExp) - .map((substring) => { - // Replace two single quote characters with one single quote character - if (substring === "''") { - return { isToken: false, value: "'" }; - } - - const firstCharacter = substring[0]; - if (firstCharacter === "'") { - return { isToken: false, value: cleanEscapedString(substring) }; - } - - if (_index3.formatters[firstCharacter]) { - return { isToken: true, value: substring }; - } - - if (firstCharacter.match(unescapedLatinCharacterRegExp)) { - throw new RangeError( - "Format string contains an unescaped latin alphabet character `" + - firstCharacter + - "`", - ); - } - - return { isToken: false, value: substring }; - }); - - // invoke localize preprocessor (only for french locales at the moment) - if (locale.localize.preprocessor) { - parts = locale.localize.preprocessor(originalDate, parts); - } - - const formatterOptions = { - firstWeekContainsDate, - weekStartsOn, - locale, - }; - - return parts - .map((part) => { - if (!part.isToken) return part.value; - - const token = part.value; - - if ( - (!options?.useAdditionalWeekYearTokens && - (0, _index5.isProtectedWeekYearToken)(token)) || - (!options?.useAdditionalDayOfYearTokens && - (0, _index5.isProtectedDayOfYearToken)(token)) - ) { - (0, _index5.warnOrThrowProtectedError)(token, formatStr, String(date)); - } - - const formatter = _index3.formatters[token[0]]; - return formatter(originalDate, token, locale.localize, formatterOptions); - }) - .join(""); -} - -function cleanEscapedString(input) { - const matched = input.match(escapedStringRegExp); - - if (!matched) { - return input; - } - - return matched[1].replace(doubleQuoteRegExp, "'"); -} - - -/***/ }), - -/***/ 1412: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getDayOfYear = getDayOfYear; -var _index = __nccwpck_require__(5671); -var _index2 = __nccwpck_require__(8537); -var _index3 = __nccwpck_require__(6439); - -/** - * The {@link getDayOfYear} function options. - */ - -/** - * @name getDayOfYear - * @category Day Helpers - * @summary Get the day of the year of the given date. - * - * @description - * Get the day of the year of the given date. - * - * @param date - The given date - * @param options - The options - * - * @returns The day of year - * - * @example - * // Which day of the year is 2 July 2014? - * const result = getDayOfYear(new Date(2014, 6, 2)) - * //=> 183 - */ -function getDayOfYear(date, options) { - const _date = (0, _index3.toDate)(date, options?.in); - const diff = (0, _index.differenceInCalendarDays)( - _date, - (0, _index2.startOfYear)(_date), - ); - const dayOfYear = diff + 1; - return dayOfYear; -} - - -/***/ }), - -/***/ 6703: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getISOWeek = getISOWeek; -var _index = __nccwpck_require__(4278); -var _index2 = __nccwpck_require__(8516); -var _index3 = __nccwpck_require__(5437); -var _index4 = __nccwpck_require__(6439); - -/** - * The {@link getISOWeek} function options. - */ - -/** - * @name getISOWeek - * @category ISO Week Helpers - * @summary Get the ISO week of the given date. - * - * @description - * Get the ISO week of the given date. - * - * ISO week-numbering year: http://en.wikipedia.org/wiki/ISO_week_date - * - * @param date - The given date - * @param options - The options - * - * @returns The ISO week - * - * @example - * // Which week of the ISO-week numbering year is 2 January 2005? - * const result = getISOWeek(new Date(2005, 0, 2)) - * //=> 53 - */ -function getISOWeek(date, options) { - const _date = (0, _index4.toDate)(date, options?.in); - const diff = - +(0, _index2.startOfISOWeek)(_date) - - +(0, _index3.startOfISOWeekYear)(_date); - - // Round the number of weeks to the nearest integer because the number of - // milliseconds in a week is not constant (e.g. it's different in the week of - // the daylight saving time clock shift). - return Math.round(diff / _index.millisecondsInWeek) + 1; -} - - -/***/ }), - -/***/ 7131: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getISOWeekYear = getISOWeekYear; -var _index = __nccwpck_require__(926); -var _index2 = __nccwpck_require__(8516); -var _index3 = __nccwpck_require__(6439); - -/** - * The {@link getISOWeekYear} function options. - */ - -/** - * @name getISOWeekYear - * @category ISO Week-Numbering Year Helpers - * @summary Get the ISO week-numbering year of the given date. - * - * @description - * Get the ISO week-numbering year of the given date, - * which always starts 3 days before the year's first Thursday. - * - * ISO week-numbering year: http://en.wikipedia.org/wiki/ISO_week_date - * - * @param date - The given date - * - * @returns The ISO week-numbering year - * - * @example - * // Which ISO-week numbering year is 2 January 2005? - * const result = getISOWeekYear(new Date(2005, 0, 2)) - * //=> 2004 - */ -function getISOWeekYear(date, options) { - const _date = (0, _index3.toDate)(date, options?.in); - const year = _date.getFullYear(); - - const fourthOfJanuaryOfNextYear = (0, _index.constructFrom)(_date, 0); - fourthOfJanuaryOfNextYear.setFullYear(year + 1, 0, 4); - fourthOfJanuaryOfNextYear.setHours(0, 0, 0, 0); - const startOfNextYear = (0, _index2.startOfISOWeek)( - fourthOfJanuaryOfNextYear, - ); - - const fourthOfJanuaryOfThisYear = (0, _index.constructFrom)(_date, 0); - fourthOfJanuaryOfThisYear.setFullYear(year, 0, 4); - fourthOfJanuaryOfThisYear.setHours(0, 0, 0, 0); - const startOfThisYear = (0, _index2.startOfISOWeek)( - fourthOfJanuaryOfThisYear, - ); - - if (_date.getTime() >= startOfNextYear.getTime()) { - return year + 1; - } else if (_date.getTime() >= startOfThisYear.getTime()) { - return year; - } else { - return year - 1; - } -} - - -/***/ }), - -/***/ 3080: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getWeek = getWeek; -var _index = __nccwpck_require__(4278); -var _index2 = __nccwpck_require__(1284); -var _index3 = __nccwpck_require__(5129); -var _index4 = __nccwpck_require__(6439); - -/** - * The {@link getWeek} function options. - */ - -/** - * @name getWeek - * @category Week Helpers - * @summary Get the local week index of the given date. - * - * @description - * Get the local week index of the given date. - * The exact calculation depends on the values of - * `options.weekStartsOn` (which is the index of the first day of the week) - * and `options.firstWeekContainsDate` (which is the day of January, which is always in - * the first week of the week-numbering year) - * - * Week numbering: https://en.wikipedia.org/wiki/Week#The_ISO_week_date_system - * - * @param date - The given date - * @param options - An object with options - * - * @returns The week - * - * @example - * // Which week of the local week numbering year is 2 January 2005 with default options? - * const result = getWeek(new Date(2005, 0, 2)) - * //=> 2 - * - * @example - * // Which week of the local week numbering year is 2 January 2005, - * // if Monday is the first day of the week, - * // and the first week of the year always contains 4 January? - * const result = getWeek(new Date(2005, 0, 2), { - * weekStartsOn: 1, - * firstWeekContainsDate: 4 - * }) - * //=> 53 - */ -function getWeek(date, options) { - const _date = (0, _index4.toDate)(date, options?.in); - const diff = - +(0, _index2.startOfWeek)(_date, options) - - +(0, _index3.startOfWeekYear)(_date, options); - - // Round the number of weeks to the nearest integer because the number of - // milliseconds in a week is not constant (e.g. it's different in the week of - // the daylight saving time clock shift). - return Math.round(diff / _index.millisecondsInWeek) + 1; -} - - -/***/ }), - -/***/ 9116: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.getWeekYear = getWeekYear; -var _index = __nccwpck_require__(5586); -var _index2 = __nccwpck_require__(926); -var _index3 = __nccwpck_require__(1284); -var _index4 = __nccwpck_require__(6439); - -/** - * The {@link getWeekYear} function options. - */ - -/** - * @name getWeekYear - * @category Week-Numbering Year Helpers - * @summary Get the local week-numbering year of the given date. - * - * @description - * Get the local week-numbering year of the given date. - * The exact calculation depends on the values of - * `options.weekStartsOn` (which is the index of the first day of the week) - * and `options.firstWeekContainsDate` (which is the day of January, which is always in - * the first week of the week-numbering year) - * - * Week numbering: https://en.wikipedia.org/wiki/Week#The_ISO_week_date_system - * - * @param date - The given date - * @param options - An object with options. - * - * @returns The local week-numbering year - * - * @example - * // Which week numbering year is 26 December 2004 with the default settings? - * const result = getWeekYear(new Date(2004, 11, 26)) - * //=> 2005 - * - * @example - * // Which week numbering year is 26 December 2004 if week starts on Saturday? - * const result = getWeekYear(new Date(2004, 11, 26), { weekStartsOn: 6 }) - * //=> 2004 - * - * @example - * // Which week numbering year is 26 December 2004 if the first week contains 4 January? - * const result = getWeekYear(new Date(2004, 11, 26), { firstWeekContainsDate: 4 }) - * //=> 2004 - */ -function getWeekYear(date, options) { - const _date = (0, _index4.toDate)(date, options?.in); - const year = _date.getFullYear(); - - const defaultOptions = (0, _index.getDefaultOptions)(); - const firstWeekContainsDate = - options?.firstWeekContainsDate ?? - options?.locale?.options?.firstWeekContainsDate ?? - defaultOptions.firstWeekContainsDate ?? - defaultOptions.locale?.options?.firstWeekContainsDate ?? - 1; - - const firstWeekOfNextYear = (0, _index2.constructFrom)( - options?.in || date, - 0, - ); - firstWeekOfNextYear.setFullYear(year + 1, 0, firstWeekContainsDate); - firstWeekOfNextYear.setHours(0, 0, 0, 0); - const startOfNextYear = (0, _index3.startOfWeek)( - firstWeekOfNextYear, - options, - ); - - const firstWeekOfThisYear = (0, _index2.constructFrom)( - options?.in || date, - 0, - ); - firstWeekOfThisYear.setFullYear(year, 0, firstWeekContainsDate); - firstWeekOfThisYear.setHours(0, 0, 0, 0); - const startOfThisYear = (0, _index3.startOfWeek)( - firstWeekOfThisYear, - options, - ); - - if (+_date >= +startOfNextYear) { - return year + 1; - } else if (+_date >= +startOfThisYear) { - return year; - } else { - return year - 1; - } -} - - -/***/ }), - -/***/ 1652: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.isDate = isDate; /** - * @name isDate - * @category Common Helpers - * @summary Is the given value a date? - * - * @description - * Returns true if the given value is an instance of Date. The function works for dates transferred across iframes. - * - * @param value - The value to check - * - * @returns True if the given value is a date - * - * @example - * // For a valid date: - * const result = isDate(new Date()) - * //=> true - * - * @example - * // For an invalid date: - * const result = isDate(new Date(NaN)) - * //=> true - * - * @example - * // For some value: - * const result = isDate('2014-02-31') - * //=> false - * - * @example - * // For an object: - * const result = isDate({}) - * //=> false - */ -function isDate(value) { - return ( - value instanceof Date || - (typeof value === "object" && - Object.prototype.toString.call(value) === "[object Date]") - ); -} - - -/***/ }), - -/***/ 6142: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.isValid = isValid; -var _index = __nccwpck_require__(1652); -var _index2 = __nccwpck_require__(6439); - -/** - * @name isValid - * @category Common Helpers - * @summary Is the given date valid? - * - * @description - * Returns false if argument is Invalid Date and true otherwise. - * Argument is converted to Date using `toDate`. See [toDate](https://date-fns.org/docs/toDate) - * Invalid Date is a Date, whose time value is NaN. - * - * Time value of Date: http://es5.github.io/#x15.9.1.1 - * - * @param date - The date to check - * - * @returns The date is valid - * - * @example - * // For the valid date: - * const result = isValid(new Date(2014, 1, 31)) - * //=> true - * - * @example - * // For the value, convertible into a date: - * const result = isValid(1393804800000) - * //=> true - * - * @example - * // For the invalid date: - * const result = isValid(new Date('')) - * //=> false - */ -function isValid(date) { - return !( - (!(0, _index.isDate)(date) && typeof date !== "number") || - isNaN(+(0, _index2.toDate)(date)) - ); -} - - -/***/ }), - -/***/ 9566: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.buildFormatLongFn = buildFormatLongFn; - -function buildFormatLongFn(args) { - return (options = {}) => { - // TODO: Remove String() - const width = options.width ? String(options.width) : args.defaultWidth; - const format = args.formats[width] || args.formats[args.defaultWidth]; - return format; - }; -} - - -/***/ }), - -/***/ 4177: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.buildLocalizeFn = buildLocalizeFn; - -/** - * The localize function argument callback which allows to convert raw value to - * the actual type. - * - * @param value - The value to convert - * - * @returns The converted value - */ - -/** - * The map of localized values for each width. - */ - -/** - * The index type of the locale unit value. It types conversion of units of - * values that don't start at 0 (i.e. quarters). - */ - -/** - * Converts the unit value to the tuple of values. - */ - -/** - * The tuple of localized era values. The first element represents BC, - * the second element represents AD. - */ - -/** - * The tuple of localized quarter values. The first element represents Q1. - */ - -/** - * The tuple of localized day values. The first element represents Sunday. - */ - -/** - * The tuple of localized month values. The first element represents January. - */ - -function buildLocalizeFn(args) { - return (value, options) => { - const context = options?.context ? String(options.context) : "standalone"; - - let valuesArray; - if (context === "formatting" && args.formattingValues) { - const defaultWidth = args.defaultFormattingWidth || args.defaultWidth; - const width = options?.width ? String(options.width) : defaultWidth; - - valuesArray = - args.formattingValues[width] || args.formattingValues[defaultWidth]; - } else { - const defaultWidth = args.defaultWidth; - const width = options?.width ? String(options.width) : args.defaultWidth; - - valuesArray = args.values[width] || args.values[defaultWidth]; - } - const index = args.argumentCallback ? args.argumentCallback(value) : value; - - // @ts-expect-error - For some reason TypeScript just don't want to match it, no matter how hard we try. I challenge you to try to remove it! - return valuesArray[index]; - }; -} - - -/***/ }), - -/***/ 3277: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.buildMatchFn = buildMatchFn; - -function buildMatchFn(args) { - return (string, options = {}) => { - const width = options.width; - - const matchPattern = - (width && args.matchPatterns[width]) || - args.matchPatterns[args.defaultMatchWidth]; - const matchResult = string.match(matchPattern); - - if (!matchResult) { - return null; - } - const matchedString = matchResult[0]; - - const parsePatterns = - (width && args.parsePatterns[width]) || - args.parsePatterns[args.defaultParseWidth]; - - const key = Array.isArray(parsePatterns) - ? findIndex(parsePatterns, (pattern) => pattern.test(matchedString)) - : // [TODO] -- I challenge you to fix the type - findKey(parsePatterns, (pattern) => pattern.test(matchedString)); - - let value; - - value = args.valueCallback ? args.valueCallback(key) : key; - value = options.valueCallback - ? // [TODO] -- I challenge you to fix the type - options.valueCallback(value) - : value; - - const rest = string.slice(matchedString.length); - - return { value, rest }; - }; -} - -function findKey(object, predicate) { - for (const key in object) { - if ( - Object.prototype.hasOwnProperty.call(object, key) && - predicate(object[key]) - ) { - return key; - } - } - return undefined; -} - -function findIndex(array, predicate) { - for (let key = 0; key < array.length; key++) { - if (predicate(array[key])) { - return key; - } - } - return undefined; -} - - -/***/ }), - -/***/ 8009: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.buildMatchPatternFn = buildMatchPatternFn; - -function buildMatchPatternFn(args) { - return (string, options = {}) => { - const matchResult = string.match(args.matchPattern); - if (!matchResult) return null; - const matchedString = matchResult[0]; - - const parseResult = string.match(args.parsePattern); - if (!parseResult) return null; - let value = args.valueCallback - ? args.valueCallback(parseResult[0]) - : parseResult[0]; - - // [TODO] I challenge you to fix the type - value = options.valueCallback ? options.valueCallback(value) : value; - - const rest = string.slice(matchedString.length); - - return { value, rest }; - }; -} - - -/***/ }), - -/***/ 9425: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.enUS = void 0; -var _index = __nccwpck_require__(5436); -var _index2 = __nccwpck_require__(5217); -var _index3 = __nccwpck_require__(1566); -var _index4 = __nccwpck_require__(2720); -var _index5 = __nccwpck_require__(6686); - -/** - * @category Locales - * @summary English locale (United States). - * @language English - * @iso-639-2 eng - * @author Sasha Koss [@kossnocorp](https://github.com/kossnocorp) - * @author Lesha Koss [@leshakoss](https://github.com/leshakoss) - */ -const enUS = (exports.enUS = { - code: "en-US", - formatDistance: _index.formatDistance, - formatLong: _index2.formatLong, - formatRelative: _index3.formatRelative, - localize: _index4.localize, - match: _index5.match, - options: { - weekStartsOn: 0 /* Sunday */, - firstWeekContainsDate: 1, - }, -}); - - -/***/ }), - -/***/ 5436: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.formatDistance = void 0; - -const formatDistanceLocale = { - lessThanXSeconds: { - one: "less than a second", - other: "less than {{count}} seconds", - }, - - xSeconds: { - one: "1 second", - other: "{{count}} seconds", - }, - - halfAMinute: "half a minute", - - lessThanXMinutes: { - one: "less than a minute", - other: "less than {{count}} minutes", - }, - - xMinutes: { - one: "1 minute", - other: "{{count}} minutes", - }, - - aboutXHours: { - one: "about 1 hour", - other: "about {{count}} hours", - }, - - xHours: { - one: "1 hour", - other: "{{count}} hours", - }, - - xDays: { - one: "1 day", - other: "{{count}} days", - }, - - aboutXWeeks: { - one: "about 1 week", - other: "about {{count}} weeks", - }, - - xWeeks: { - one: "1 week", - other: "{{count}} weeks", - }, - - aboutXMonths: { - one: "about 1 month", - other: "about {{count}} months", - }, - - xMonths: { - one: "1 month", - other: "{{count}} months", - }, - - aboutXYears: { - one: "about 1 year", - other: "about {{count}} years", - }, - - xYears: { - one: "1 year", - other: "{{count}} years", - }, - - overXYears: { - one: "over 1 year", - other: "over {{count}} years", - }, - - almostXYears: { - one: "almost 1 year", - other: "almost {{count}} years", - }, -}; - -const formatDistance = (token, count, options) => { - let result; - - const tokenValue = formatDistanceLocale[token]; - if (typeof tokenValue === "string") { - result = tokenValue; - } else if (count === 1) { - result = tokenValue.one; - } else { - result = tokenValue.other.replace("{{count}}", count.toString()); - } - - if (options?.addSuffix) { - if (options.comparison && options.comparison > 0) { - return "in " + result; - } else { - return result + " ago"; - } - } - - return result; -}; -exports.formatDistance = formatDistance; - - -/***/ }), - -/***/ 5217: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.formatLong = void 0; -var _index = __nccwpck_require__(9566); - -const dateFormats = { - full: "EEEE, MMMM do, y", - long: "MMMM do, y", - medium: "MMM d, y", - short: "MM/dd/yyyy", -}; - -const timeFormats = { - full: "h:mm:ss a zzzz", - long: "h:mm:ss a z", - medium: "h:mm:ss a", - short: "h:mm a", -}; - -const dateTimeFormats = { - full: "{{date}} 'at' {{time}}", - long: "{{date}} 'at' {{time}}", - medium: "{{date}}, {{time}}", - short: "{{date}}, {{time}}", -}; - -const formatLong = (exports.formatLong = { - date: (0, _index.buildFormatLongFn)({ - formats: dateFormats, - defaultWidth: "full", - }), - - time: (0, _index.buildFormatLongFn)({ - formats: timeFormats, - defaultWidth: "full", - }), - - dateTime: (0, _index.buildFormatLongFn)({ - formats: dateTimeFormats, - defaultWidth: "full", - }), -}); - - -/***/ }), - -/***/ 1566: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -exports.formatRelative = void 0; - -const formatRelativeLocale = { - lastWeek: "'last' eeee 'at' p", - yesterday: "'yesterday at' p", - today: "'today at' p", - tomorrow: "'tomorrow at' p", - nextWeek: "eeee 'at' p", - other: "P", -}; - -const formatRelative = (token, _date, _baseDate, _options) => - formatRelativeLocale[token]; -exports.formatRelative = formatRelative; - - -/***/ }), - -/***/ 2720: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.localize = void 0; -var _index = __nccwpck_require__(4177); - -const eraValues = { - narrow: ["B", "A"], - abbreviated: ["BC", "AD"], - wide: ["Before Christ", "Anno Domini"], -}; - -const quarterValues = { - narrow: ["1", "2", "3", "4"], - abbreviated: ["Q1", "Q2", "Q3", "Q4"], - wide: ["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"], -}; - -// Note: in English, the names of days of the week and months are capitalized. -// If you are making a new locale based on this one, check if the same is true for the language you're working on. -// Generally, formatted dates should look like they are in the middle of a sentence, -// e.g. in Spanish language the weekdays and months should be in the lowercase. -const monthValues = { - narrow: ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], - abbreviated: [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - ], - - wide: [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ], -}; - -const dayValues = { - narrow: ["S", "M", "T", "W", "T", "F", "S"], - short: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], - abbreviated: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - wide: [ - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - ], -}; - -const dayPeriodValues = { - narrow: { - am: "a", - pm: "p", - midnight: "mi", - noon: "n", - morning: "morning", - afternoon: "afternoon", - evening: "evening", - night: "night", - }, - abbreviated: { - am: "AM", - pm: "PM", - midnight: "midnight", - noon: "noon", - morning: "morning", - afternoon: "afternoon", - evening: "evening", - night: "night", - }, - wide: { - am: "a.m.", - pm: "p.m.", - midnight: "midnight", - noon: "noon", - morning: "morning", - afternoon: "afternoon", - evening: "evening", - night: "night", - }, -}; - -const formattingDayPeriodValues = { - narrow: { - am: "a", - pm: "p", - midnight: "mi", - noon: "n", - morning: "in the morning", - afternoon: "in the afternoon", - evening: "in the evening", - night: "at night", - }, - abbreviated: { - am: "AM", - pm: "PM", - midnight: "midnight", - noon: "noon", - morning: "in the morning", - afternoon: "in the afternoon", - evening: "in the evening", - night: "at night", - }, - wide: { - am: "a.m.", - pm: "p.m.", - midnight: "midnight", - noon: "noon", - morning: "in the morning", - afternoon: "in the afternoon", - evening: "in the evening", - night: "at night", - }, -}; - -const ordinalNumber = (dirtyNumber, _options) => { - const number = Number(dirtyNumber); - - // If ordinal numbers depend on context, for example, - // if they are different for different grammatical genders, - // use `options.unit`. - // - // `unit` can be 'year', 'quarter', 'month', 'week', 'date', 'dayOfYear', - // 'day', 'hour', 'minute', 'second'. - - const rem100 = number % 100; - if (rem100 > 20 || rem100 < 10) { - switch (rem100 % 10) { - case 1: - return number + "st"; - case 2: - return number + "nd"; - case 3: - return number + "rd"; - } - } - return number + "th"; -}; - -const localize = (exports.localize = { - ordinalNumber, - - era: (0, _index.buildLocalizeFn)({ - values: eraValues, - defaultWidth: "wide", - }), - - quarter: (0, _index.buildLocalizeFn)({ - values: quarterValues, - defaultWidth: "wide", - argumentCallback: (quarter) => quarter - 1, - }), - - month: (0, _index.buildLocalizeFn)({ - values: monthValues, - defaultWidth: "wide", - }), - - day: (0, _index.buildLocalizeFn)({ - values: dayValues, - defaultWidth: "wide", - }), - - dayPeriod: (0, _index.buildLocalizeFn)({ - values: dayPeriodValues, - defaultWidth: "wide", - formattingValues: formattingDayPeriodValues, - defaultFormattingWidth: "wide", - }), -}); - - -/***/ }), - -/***/ 6686: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.match = void 0; - -var _index = __nccwpck_require__(3277); -var _index2 = __nccwpck_require__(8009); - -const matchOrdinalNumberPattern = /^(\d+)(th|st|nd|rd)?/i; -const parseOrdinalNumberPattern = /\d+/i; - -const matchEraPatterns = { - narrow: /^(b|a)/i, - abbreviated: /^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i, - wide: /^(before christ|before common era|anno domini|common era)/i, -}; -const parseEraPatterns = { - any: [/^b/i, /^(a|c)/i], -}; - -const matchQuarterPatterns = { - narrow: /^[1234]/i, - abbreviated: /^q[1234]/i, - wide: /^[1234](th|st|nd|rd)? quarter/i, -}; -const parseQuarterPatterns = { - any: [/1/i, /2/i, /3/i, /4/i], -}; - -const matchMonthPatterns = { - narrow: /^[jfmasond]/i, - abbreviated: /^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i, - wide: /^(january|february|march|april|may|june|july|august|september|october|november|december)/i, -}; -const parseMonthPatterns = { - narrow: [ - /^j/i, - /^f/i, - /^m/i, - /^a/i, - /^m/i, - /^j/i, - /^j/i, - /^a/i, - /^s/i, - /^o/i, - /^n/i, - /^d/i, - ], - - any: [ - /^ja/i, - /^f/i, - /^mar/i, - /^ap/i, - /^may/i, - /^jun/i, - /^jul/i, - /^au/i, - /^s/i, - /^o/i, - /^n/i, - /^d/i, - ], -}; - -const matchDayPatterns = { - narrow: /^[smtwf]/i, - short: /^(su|mo|tu|we|th|fr|sa)/i, - abbreviated: /^(sun|mon|tue|wed|thu|fri|sat)/i, - wide: /^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i, -}; -const parseDayPatterns = { - narrow: [/^s/i, /^m/i, /^t/i, /^w/i, /^t/i, /^f/i, /^s/i], - any: [/^su/i, /^m/i, /^tu/i, /^w/i, /^th/i, /^f/i, /^sa/i], -}; - -const matchDayPeriodPatterns = { - narrow: /^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i, - any: /^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i, -}; -const parseDayPeriodPatterns = { - any: { - am: /^a/i, - pm: /^p/i, - midnight: /^mi/i, - noon: /^no/i, - morning: /morning/i, - afternoon: /afternoon/i, - evening: /evening/i, - night: /night/i, - }, -}; - -const match = (exports.match = { - ordinalNumber: (0, _index2.buildMatchPatternFn)({ - matchPattern: matchOrdinalNumberPattern, - parsePattern: parseOrdinalNumberPattern, - valueCallback: (value) => parseInt(value, 10), - }), - - era: (0, _index.buildMatchFn)({ - matchPatterns: matchEraPatterns, - defaultMatchWidth: "wide", - parsePatterns: parseEraPatterns, - defaultParseWidth: "any", - }), - - quarter: (0, _index.buildMatchFn)({ - matchPatterns: matchQuarterPatterns, - defaultMatchWidth: "wide", - parsePatterns: parseQuarterPatterns, - defaultParseWidth: "any", - valueCallback: (index) => index + 1, - }), - - month: (0, _index.buildMatchFn)({ - matchPatterns: matchMonthPatterns, - defaultMatchWidth: "wide", - parsePatterns: parseMonthPatterns, - defaultParseWidth: "any", - }), - - day: (0, _index.buildMatchFn)({ - matchPatterns: matchDayPatterns, - defaultMatchWidth: "wide", - parsePatterns: parseDayPatterns, - defaultParseWidth: "any", - }), - - dayPeriod: (0, _index.buildMatchFn)({ - matchPatterns: matchDayPeriodPatterns, - defaultMatchWidth: "any", - parsePatterns: parseDayPeriodPatterns, - defaultParseWidth: "any", - }), -}); - - -/***/ }), - -/***/ 5951: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfDay = startOfDay; -var _index = __nccwpck_require__(6439); - -/** - * The {@link startOfDay} function options. - */ - -/** - * @name startOfDay - * @category Day Helpers - * @summary Return the start of a day for the given date. - * - * @description - * Return the start of a day for the given date. - * The result will be in the local timezone. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param date - The original date - * @param options - The options - * - * @returns The start of a day - * - * @example - * // The start of a day for 2 September 2014 11:55:00: - * const result = startOfDay(new Date(2014, 8, 2, 11, 55, 0)) - * //=> Tue Sep 02 2014 00:00:00 - */ -function startOfDay(date, options) { - const _date = (0, _index.toDate)(date, options?.in); - _date.setHours(0, 0, 0, 0); - return _date; -} - - -/***/ }), - -/***/ 8516: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfISOWeek = startOfISOWeek; -var _index = __nccwpck_require__(1284); - -/** - * The {@link startOfISOWeek} function options. - */ - -/** - * @name startOfISOWeek - * @category ISO Week Helpers - * @summary Return the start of an ISO week for the given date. - * - * @description - * Return the start of an ISO week for the given date. - * The result will be in the local timezone. - * - * ISO week-numbering year: http://en.wikipedia.org/wiki/ISO_week_date - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param date - The original date - * @param options - An object with options - * - * @returns The start of an ISO week - * - * @example - * // The start of an ISO week for 2 September 2014 11:55:00: - * const result = startOfISOWeek(new Date(2014, 8, 2, 11, 55, 0)) - * //=> Mon Sep 01 2014 00:00:00 - */ -function startOfISOWeek(date, options) { - return (0, _index.startOfWeek)(date, { ...options, weekStartsOn: 1 }); -} - - -/***/ }), - -/***/ 5437: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfISOWeekYear = startOfISOWeekYear; -var _index = __nccwpck_require__(926); -var _index2 = __nccwpck_require__(7131); -var _index3 = __nccwpck_require__(8516); - -/** - * The {@link startOfISOWeekYear} function options. - */ - -/** - * @name startOfISOWeekYear - * @category ISO Week-Numbering Year Helpers - * @summary Return the start of an ISO week-numbering year for the given date. - * - * @description - * Return the start of an ISO week-numbering year, - * which always starts 3 days before the year's first Thursday. - * The result will be in the local timezone. - * - * ISO week-numbering year: http://en.wikipedia.org/wiki/ISO_week_date - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param date - The original date - * @param options - An object with options - * - * @returns The start of an ISO week-numbering year - * - * @example - * // The start of an ISO week-numbering year for 2 July 2005: - * const result = startOfISOWeekYear(new Date(2005, 6, 2)) - * //=> Mon Jan 03 2005 00:00:00 - */ -function startOfISOWeekYear(date, options) { - const year = (0, _index2.getISOWeekYear)(date, options); - const fourthOfJanuary = (0, _index.constructFrom)(options?.in || date, 0); - fourthOfJanuary.setFullYear(year, 0, 4); - fourthOfJanuary.setHours(0, 0, 0, 0); - return (0, _index3.startOfISOWeek)(fourthOfJanuary); -} - - -/***/ }), - -/***/ 1284: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfWeek = startOfWeek; -var _index = __nccwpck_require__(5586); -var _index2 = __nccwpck_require__(6439); - -/** - * The {@link startOfWeek} function options. - */ - -/** - * @name startOfWeek - * @category Week Helpers - * @summary Return the start of a week for the given date. - * - * @description - * Return the start of a week for the given date. - * The result will be in the local timezone. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param date - The original date - * @param options - An object with options - * - * @returns The start of a week - * - * @example - * // The start of a week for 2 September 2014 11:55:00: - * const result = startOfWeek(new Date(2014, 8, 2, 11, 55, 0)) - * //=> Sun Aug 31 2014 00:00:00 - * - * @example - * // If the week starts on Monday, the start of the week for 2 September 2014 11:55:00: - * const result = startOfWeek(new Date(2014, 8, 2, 11, 55, 0), { weekStartsOn: 1 }) - * //=> Mon Sep 01 2014 00:00:00 - */ -function startOfWeek(date, options) { - const defaultOptions = (0, _index.getDefaultOptions)(); - const weekStartsOn = - options?.weekStartsOn ?? - options?.locale?.options?.weekStartsOn ?? - defaultOptions.weekStartsOn ?? - defaultOptions.locale?.options?.weekStartsOn ?? - 0; - - const _date = (0, _index2.toDate)(date, options?.in); - const day = _date.getDay(); - const diff = (day < weekStartsOn ? 7 : 0) + day - weekStartsOn; - - _date.setDate(_date.getDate() - diff); - _date.setHours(0, 0, 0, 0); - return _date; -} - - -/***/ }), - -/***/ 5129: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfWeekYear = startOfWeekYear; -var _index = __nccwpck_require__(5586); -var _index2 = __nccwpck_require__(926); -var _index3 = __nccwpck_require__(9116); -var _index4 = __nccwpck_require__(1284); - -/** - * The {@link startOfWeekYear} function options. - */ - -/** - * @name startOfWeekYear - * @category Week-Numbering Year Helpers - * @summary Return the start of a local week-numbering year for the given date. - * - * @description - * Return the start of a local week-numbering year. - * The exact calculation depends on the values of - * `options.weekStartsOn` (which is the index of the first day of the week) - * and `options.firstWeekContainsDate` (which is the day of January, which is always in - * the first week of the week-numbering year) - * - * Week numbering: https://en.wikipedia.org/wiki/Week#The_ISO_week_date_system - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type. - * - * @param date - The original date - * @param options - An object with options - * - * @returns The start of a week-numbering year - * - * @example - * // The start of an a week-numbering year for 2 July 2005 with default settings: - * const result = startOfWeekYear(new Date(2005, 6, 2)) - * //=> Sun Dec 26 2004 00:00:00 - * - * @example - * // The start of a week-numbering year for 2 July 2005 - * // if Monday is the first day of week - * // and 4 January is always in the first week of the year: - * const result = startOfWeekYear(new Date(2005, 6, 2), { - * weekStartsOn: 1, - * firstWeekContainsDate: 4 - * }) - * //=> Mon Jan 03 2005 00:00:00 - */ -function startOfWeekYear(date, options) { - const defaultOptions = (0, _index.getDefaultOptions)(); - const firstWeekContainsDate = - options?.firstWeekContainsDate ?? - options?.locale?.options?.firstWeekContainsDate ?? - defaultOptions.firstWeekContainsDate ?? - defaultOptions.locale?.options?.firstWeekContainsDate ?? - 1; - - const year = (0, _index3.getWeekYear)(date, options); - const firstWeek = (0, _index2.constructFrom)(options?.in || date, 0); - firstWeek.setFullYear(year, 0, firstWeekContainsDate); - firstWeek.setHours(0, 0, 0, 0); - const _date = (0, _index4.startOfWeek)(firstWeek, options); - return _date; -} - - -/***/ }), - -/***/ 8537: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.startOfYear = startOfYear; -var _index = __nccwpck_require__(6439); - -/** - * The {@link startOfYear} function options. - */ - -/** - * @name startOfYear - * @category Year Helpers - * @summary Return the start of a year for the given date. - * - * @description - * Return the start of a year for the given date. - * The result will be in the local timezone. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param date - The original date - * @param options - The options - * - * @returns The start of a year - * - * @example - * // The start of a year for 2 September 2014 11:55:00: - * const result = startOfYear(new Date(2014, 8, 2, 11, 55, 00)) - * //=> Wed Jan 01 2014 00:00:00 - */ -function startOfYear(date, options) { - const date_ = (0, _index.toDate)(date, options?.in); - date_.setFullYear(date_.getFullYear(), 0, 1); - date_.setHours(0, 0, 0, 0); - return date_; -} - - -/***/ }), - -/***/ 6439: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - -exports.toDate = toDate; -var _index = __nccwpck_require__(926); - -/** - * @name toDate - * @category Common Helpers - * @summary Convert the given argument to an instance of Date. - * - * @description - * Convert the given argument to an instance of Date. - * - * If the argument is an instance of Date, the function returns its clone. - * - * If the argument is a number, it is treated as a timestamp. - * - * If the argument is none of the above, the function returns Invalid Date. - * - * Starting from v3.7.0, it clones a date using `[Symbol.for("constructDateFrom")]` - * enabling to transfer extra properties from the reference date to the new date. - * It's useful for extensions like [`TZDate`](https://github.com/date-fns/tz) - * that accept a time zone as a constructor argument. - * - * **Note**: *all* Date arguments passed to any *date-fns* function is processed by `toDate`. - * - * @typeParam DateType - The `Date` type, the function operates on. Gets inferred from passed arguments. Allows to use extensions like [`UTCDate`](https://github.com/date-fns/utc). - * @typeParam ResultDate - The result `Date` type, it is the type returned from the context function if it is passed, or inferred from the arguments. - * - * @param argument - The value to convert - * - * @returns The parsed date in the local time zone - * - * @example - * // Clone the date: - * const result = toDate(new Date(2014, 1, 11, 11, 30, 30)) - * //=> Tue Feb 11 2014 11:30:30 - * - * @example - * // Convert the timestamp to date: - * const result = toDate(1392098430000) - * //=> Tue Feb 11 2014 11:30:30 - */ -function toDate(argument, context) { - // [TODO] Get rid of `toDate` or `constructFrom`? - return (0, _index.constructFrom)(context || argument, argument); -} - - -/***/ }), - -/***/ 2020: -/***/ ((module) => { - -"use strict"; -module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"],[[47,47],"disallowed_STD3_valid"],[[48,57],"valid"],[[58,64],"disallowed_STD3_valid"],[[65,65],"mapped",[97]],[[66,66],"mapped",[98]],[[67,67],"mapped",[99]],[[68,68],"mapped",[100]],[[69,69],"mapped",[101]],[[70,70],"mapped",[102]],[[71,71],"mapped",[103]],[[72,72],"mapped",[104]],[[73,73],"mapped",[105]],[[74,74],"mapped",[106]],[[75,75],"mapped",[107]],[[76,76],"mapped",[108]],[[77,77],"mapped",[109]],[[78,78],"mapped",[110]],[[79,79],"mapped",[111]],[[80,80],"mapped",[112]],[[81,81],"mapped",[113]],[[82,82],"mapped",[114]],[[83,83],"mapped",[115]],[[84,84],"mapped",[116]],[[85,85],"mapped",[117]],[[86,86],"mapped",[118]],[[87,87],"mapped",[119]],[[88,88],"mapped",[120]],[[89,89],"mapped",[121]],[[90,90],"mapped",[122]],[[91,96],"disallowed_STD3_valid"],[[97,122],"valid"],[[123,127],"disallowed_STD3_valid"],[[128,159],"disallowed"],[[160,160],"disallowed_STD3_mapped",[32]],[[161,167],"valid",[],"NV8"],[[168,168],"disallowed_STD3_mapped",[32,776]],[[169,169],"valid",[],"NV8"],[[170,170],"mapped",[97]],[[171,172],"valid",[],"NV8"],[[173,173],"ignored"],[[174,174],"valid",[],"NV8"],[[175,175],"disallowed_STD3_mapped",[32,772]],[[176,177],"valid",[],"NV8"],[[178,178],"mapped",[50]],[[179,179],"mapped",[51]],[[180,180],"disallowed_STD3_mapped",[32,769]],[[181,181],"mapped",[956]],[[182,182],"valid",[],"NV8"],[[183,183],"valid"],[[184,184],"disallowed_STD3_mapped",[32,807]],[[185,185],"mapped",[49]],[[186,186],"mapped",[111]],[[187,187],"valid",[],"NV8"],[[188,188],"mapped",[49,8260,52]],[[189,189],"mapped",[49,8260,50]],[[190,190],"mapped",[51,8260,52]],[[191,191],"valid",[],"NV8"],[[192,192],"mapped",[224]],[[193,193],"mapped",[225]],[[194,194],"mapped",[226]],[[195,195],"mapped",[227]],[[196,196],"mapped",[228]],[[197,197],"mapped",[229]],[[198,198],"mapped",[230]],[[199,199],"mapped",[231]],[[200,200],"mapped",[232]],[[201,201],"mapped",[233]],[[202,202],"mapped",[234]],[[203,203],"mapped",[235]],[[204,204],"mapped",[236]],[[205,205],"mapped",[237]],[[206,206],"mapped",[238]],[[207,207],"mapped",[239]],[[208,208],"mapped",[240]],[[209,209],"mapped",[241]],[[210,210],"mapped",[242]],[[211,211],"mapped",[243]],[[212,212],"mapped",[244]],[[213,213],"mapped",[245]],[[214,214],"mapped",[246]],[[215,215],"valid",[],"NV8"],[[216,216],"mapped",[248]],[[217,217],"mapped",[249]],[[218,218],"mapped",[250]],[[219,219],"mapped",[251]],[[220,220],"mapped",[252]],[[221,221],"mapped",[253]],[[222,222],"mapped",[254]],[[223,223],"deviation",[115,115]],[[224,246],"valid"],[[247,247],"valid",[],"NV8"],[[248,255],"valid"],[[256,256],"mapped",[257]],[[257,257],"valid"],[[258,258],"mapped",[259]],[[259,259],"valid"],[[260,260],"mapped",[261]],[[261,261],"valid"],[[262,262],"mapped",[263]],[[263,263],"valid"],[[264,264],"mapped",[265]],[[265,265],"valid"],[[266,266],"mapped",[267]],[[267,267],"valid"],[[268,268],"mapped",[269]],[[269,269],"valid"],[[270,270],"mapped",[271]],[[271,271],"valid"],[[272,272],"mapped",[273]],[[273,273],"valid"],[[274,274],"mapped",[275]],[[275,275],"valid"],[[276,276],"mapped",[277]],[[277,277],"valid"],[[278,278],"mapped",[279]],[[279,279],"valid"],[[280,280],"mapped",[281]],[[281,281],"valid"],[[282,282],"mapped",[283]],[[283,283],"valid"],[[284,284],"mapped",[285]],[[285,285],"valid"],[[286,286],"mapped",[287]],[[287,287],"valid"],[[288,288],"mapped",[289]],[[289,289],"valid"],[[290,290],"mapped",[291]],[[291,291],"valid"],[[292,292],"mapped",[293]],[[293,293],"valid"],[[294,294],"mapped",[295]],[[295,295],"valid"],[[296,296],"mapped",[297]],[[297,297],"valid"],[[298,298],"mapped",[299]],[[299,299],"valid"],[[300,300],"mapped",[301]],[[301,301],"valid"],[[302,302],"mapped",[303]],[[303,303],"valid"],[[304,304],"mapped",[105,775]],[[305,305],"valid"],[[306,307],"mapped",[105,106]],[[308,308],"mapped",[309]],[[309,309],"valid"],[[310,310],"mapped",[311]],[[311,312],"valid"],[[313,313],"mapped",[314]],[[314,314],"valid"],[[315,315],"mapped",[316]],[[316,316],"valid"],[[317,317],"mapped",[318]],[[318,318],"valid"],[[319,320],"mapped",[108,183]],[[321,321],"mapped",[322]],[[322,322],"valid"],[[323,323],"mapped",[324]],[[324,324],"valid"],[[325,325],"mapped",[326]],[[326,326],"valid"],[[327,327],"mapped",[328]],[[328,328],"valid"],[[329,329],"mapped",[700,110]],[[330,330],"mapped",[331]],[[331,331],"valid"],[[332,332],"mapped",[333]],[[333,333],"valid"],[[334,334],"mapped",[335]],[[335,335],"valid"],[[336,336],"mapped",[337]],[[337,337],"valid"],[[338,338],"mapped",[339]],[[339,339],"valid"],[[340,340],"mapped",[341]],[[341,341],"valid"],[[342,342],"mapped",[343]],[[343,343],"valid"],[[344,344],"mapped",[345]],[[345,345],"valid"],[[346,346],"mapped",[347]],[[347,347],"valid"],[[348,348],"mapped",[349]],[[349,349],"valid"],[[350,350],"mapped",[351]],[[351,351],"valid"],[[352,352],"mapped",[353]],[[353,353],"valid"],[[354,354],"mapped",[355]],[[355,355],"valid"],[[356,356],"mapped",[357]],[[357,357],"valid"],[[358,358],"mapped",[359]],[[359,359],"valid"],[[360,360],"mapped",[361]],[[361,361],"valid"],[[362,362],"mapped",[363]],[[363,363],"valid"],[[364,364],"mapped",[365]],[[365,365],"valid"],[[366,366],"mapped",[367]],[[367,367],"valid"],[[368,368],"mapped",[369]],[[369,369],"valid"],[[370,370],"mapped",[371]],[[371,371],"valid"],[[372,372],"mapped",[373]],[[373,373],"valid"],[[374,374],"mapped",[375]],[[375,375],"valid"],[[376,376],"mapped",[255]],[[377,377],"mapped",[378]],[[378,378],"valid"],[[379,379],"mapped",[380]],[[380,380],"valid"],[[381,381],"mapped",[382]],[[382,382],"valid"],[[383,383],"mapped",[115]],[[384,384],"valid"],[[385,385],"mapped",[595]],[[386,386],"mapped",[387]],[[387,387],"valid"],[[388,388],"mapped",[389]],[[389,389],"valid"],[[390,390],"mapped",[596]],[[391,391],"mapped",[392]],[[392,392],"valid"],[[393,393],"mapped",[598]],[[394,394],"mapped",[599]],[[395,395],"mapped",[396]],[[396,397],"valid"],[[398,398],"mapped",[477]],[[399,399],"mapped",[601]],[[400,400],"mapped",[603]],[[401,401],"mapped",[402]],[[402,402],"valid"],[[403,403],"mapped",[608]],[[404,404],"mapped",[611]],[[405,405],"valid"],[[406,406],"mapped",[617]],[[407,407],"mapped",[616]],[[408,408],"mapped",[409]],[[409,411],"valid"],[[412,412],"mapped",[623]],[[413,413],"mapped",[626]],[[414,414],"valid"],[[415,415],"mapped",[629]],[[416,416],"mapped",[417]],[[417,417],"valid"],[[418,418],"mapped",[419]],[[419,419],"valid"],[[420,420],"mapped",[421]],[[421,421],"valid"],[[422,422],"mapped",[640]],[[423,423],"mapped",[424]],[[424,424],"valid"],[[425,425],"mapped",[643]],[[426,427],"valid"],[[428,428],"mapped",[429]],[[429,429],"valid"],[[430,430],"mapped",[648]],[[431,431],"mapped",[432]],[[432,432],"valid"],[[433,433],"mapped",[650]],[[434,434],"mapped",[651]],[[435,435],"mapped",[436]],[[436,436],"valid"],[[437,437],"mapped",[438]],[[438,438],"valid"],[[439,439],"mapped",[658]],[[440,440],"mapped",[441]],[[441,443],"valid"],[[444,444],"mapped",[445]],[[445,451],"valid"],[[452,454],"mapped",[100,382]],[[455,457],"mapped",[108,106]],[[458,460],"mapped",[110,106]],[[461,461],"mapped",[462]],[[462,462],"valid"],[[463,463],"mapped",[464]],[[464,464],"valid"],[[465,465],"mapped",[466]],[[466,466],"valid"],[[467,467],"mapped",[468]],[[468,468],"valid"],[[469,469],"mapped",[470]],[[470,470],"valid"],[[471,471],"mapped",[472]],[[472,472],"valid"],[[473,473],"mapped",[474]],[[474,474],"valid"],[[475,475],"mapped",[476]],[[476,477],"valid"],[[478,478],"mapped",[479]],[[479,479],"valid"],[[480,480],"mapped",[481]],[[481,481],"valid"],[[482,482],"mapped",[483]],[[483,483],"valid"],[[484,484],"mapped",[485]],[[485,485],"valid"],[[486,486],"mapped",[487]],[[487,487],"valid"],[[488,488],"mapped",[489]],[[489,489],"valid"],[[490,490],"mapped",[491]],[[491,491],"valid"],[[492,492],"mapped",[493]],[[493,493],"valid"],[[494,494],"mapped",[495]],[[495,496],"valid"],[[497,499],"mapped",[100,122]],[[500,500],"mapped",[501]],[[501,501],"valid"],[[502,502],"mapped",[405]],[[503,503],"mapped",[447]],[[504,504],"mapped",[505]],[[505,505],"valid"],[[506,506],"mapped",[507]],[[507,507],"valid"],[[508,508],"mapped",[509]],[[509,509],"valid"],[[510,510],"mapped",[511]],[[511,511],"valid"],[[512,512],"mapped",[513]],[[513,513],"valid"],[[514,514],"mapped",[515]],[[515,515],"valid"],[[516,516],"mapped",[517]],[[517,517],"valid"],[[518,518],"mapped",[519]],[[519,519],"valid"],[[520,520],"mapped",[521]],[[521,521],"valid"],[[522,522],"mapped",[523]],[[523,523],"valid"],[[524,524],"mapped",[525]],[[525,525],"valid"],[[526,526],"mapped",[527]],[[527,527],"valid"],[[528,528],"mapped",[529]],[[529,529],"valid"],[[530,530],"mapped",[531]],[[531,531],"valid"],[[532,532],"mapped",[533]],[[533,533],"valid"],[[534,534],"mapped",[535]],[[535,535],"valid"],[[536,536],"mapped",[537]],[[537,537],"valid"],[[538,538],"mapped",[539]],[[539,539],"valid"],[[540,540],"mapped",[541]],[[541,541],"valid"],[[542,542],"mapped",[543]],[[543,543],"valid"],[[544,544],"mapped",[414]],[[545,545],"valid"],[[546,546],"mapped",[547]],[[547,547],"valid"],[[548,548],"mapped",[549]],[[549,549],"valid"],[[550,550],"mapped",[551]],[[551,551],"valid"],[[552,552],"mapped",[553]],[[553,553],"valid"],[[554,554],"mapped",[555]],[[555,555],"valid"],[[556,556],"mapped",[557]],[[557,557],"valid"],[[558,558],"mapped",[559]],[[559,559],"valid"],[[560,560],"mapped",[561]],[[561,561],"valid"],[[562,562],"mapped",[563]],[[563,563],"valid"],[[564,566],"valid"],[[567,569],"valid"],[[570,570],"mapped",[11365]],[[571,571],"mapped",[572]],[[572,572],"valid"],[[573,573],"mapped",[410]],[[574,574],"mapped",[11366]],[[575,576],"valid"],[[577,577],"mapped",[578]],[[578,578],"valid"],[[579,579],"mapped",[384]],[[580,580],"mapped",[649]],[[581,581],"mapped",[652]],[[582,582],"mapped",[583]],[[583,583],"valid"],[[584,584],"mapped",[585]],[[585,585],"valid"],[[586,586],"mapped",[587]],[[587,587],"valid"],[[588,588],"mapped",[589]],[[589,589],"valid"],[[590,590],"mapped",[591]],[[591,591],"valid"],[[592,680],"valid"],[[681,685],"valid"],[[686,687],"valid"],[[688,688],"mapped",[104]],[[689,689],"mapped",[614]],[[690,690],"mapped",[106]],[[691,691],"mapped",[114]],[[692,692],"mapped",[633]],[[693,693],"mapped",[635]],[[694,694],"mapped",[641]],[[695,695],"mapped",[119]],[[696,696],"mapped",[121]],[[697,705],"valid"],[[706,709],"valid",[],"NV8"],[[710,721],"valid"],[[722,727],"valid",[],"NV8"],[[728,728],"disallowed_STD3_mapped",[32,774]],[[729,729],"disallowed_STD3_mapped",[32,775]],[[730,730],"disallowed_STD3_mapped",[32,778]],[[731,731],"disallowed_STD3_mapped",[32,808]],[[732,732],"disallowed_STD3_mapped",[32,771]],[[733,733],"disallowed_STD3_mapped",[32,779]],[[734,734],"valid",[],"NV8"],[[735,735],"valid",[],"NV8"],[[736,736],"mapped",[611]],[[737,737],"mapped",[108]],[[738,738],"mapped",[115]],[[739,739],"mapped",[120]],[[740,740],"mapped",[661]],[[741,745],"valid",[],"NV8"],[[746,747],"valid",[],"NV8"],[[748,748],"valid"],[[749,749],"valid",[],"NV8"],[[750,750],"valid"],[[751,767],"valid",[],"NV8"],[[768,831],"valid"],[[832,832],"mapped",[768]],[[833,833],"mapped",[769]],[[834,834],"valid"],[[835,835],"mapped",[787]],[[836,836],"mapped",[776,769]],[[837,837],"mapped",[953]],[[838,846],"valid"],[[847,847],"ignored"],[[848,855],"valid"],[[856,860],"valid"],[[861,863],"valid"],[[864,865],"valid"],[[866,866],"valid"],[[867,879],"valid"],[[880,880],"mapped",[881]],[[881,881],"valid"],[[882,882],"mapped",[883]],[[883,883],"valid"],[[884,884],"mapped",[697]],[[885,885],"valid"],[[886,886],"mapped",[887]],[[887,887],"valid"],[[888,889],"disallowed"],[[890,890],"disallowed_STD3_mapped",[32,953]],[[891,893],"valid"],[[894,894],"disallowed_STD3_mapped",[59]],[[895,895],"mapped",[1011]],[[896,899],"disallowed"],[[900,900],"disallowed_STD3_mapped",[32,769]],[[901,901],"disallowed_STD3_mapped",[32,776,769]],[[902,902],"mapped",[940]],[[903,903],"mapped",[183]],[[904,904],"mapped",[941]],[[905,905],"mapped",[942]],[[906,906],"mapped",[943]],[[907,907],"disallowed"],[[908,908],"mapped",[972]],[[909,909],"disallowed"],[[910,910],"mapped",[973]],[[911,911],"mapped",[974]],[[912,912],"valid"],[[913,913],"mapped",[945]],[[914,914],"mapped",[946]],[[915,915],"mapped",[947]],[[916,916],"mapped",[948]],[[917,917],"mapped",[949]],[[918,918],"mapped",[950]],[[919,919],"mapped",[951]],[[920,920],"mapped",[952]],[[921,921],"mapped",[953]],[[922,922],"mapped",[954]],[[923,923],"mapped",[955]],[[924,924],"mapped",[956]],[[925,925],"mapped",[957]],[[926,926],"mapped",[958]],[[927,927],"mapped",[959]],[[928,928],"mapped",[960]],[[929,929],"mapped",[961]],[[930,930],"disallowed"],[[931,931],"mapped",[963]],[[932,932],"mapped",[964]],[[933,933],"mapped",[965]],[[934,934],"mapped",[966]],[[935,935],"mapped",[967]],[[936,936],"mapped",[968]],[[937,937],"mapped",[969]],[[938,938],"mapped",[970]],[[939,939],"mapped",[971]],[[940,961],"valid"],[[962,962],"deviation",[963]],[[963,974],"valid"],[[975,975],"mapped",[983]],[[976,976],"mapped",[946]],[[977,977],"mapped",[952]],[[978,978],"mapped",[965]],[[979,979],"mapped",[973]],[[980,980],"mapped",[971]],[[981,981],"mapped",[966]],[[982,982],"mapped",[960]],[[983,983],"valid"],[[984,984],"mapped",[985]],[[985,985],"valid"],[[986,986],"mapped",[987]],[[987,987],"valid"],[[988,988],"mapped",[989]],[[989,989],"valid"],[[990,990],"mapped",[991]],[[991,991],"valid"],[[992,992],"mapped",[993]],[[993,993],"valid"],[[994,994],"mapped",[995]],[[995,995],"valid"],[[996,996],"mapped",[997]],[[997,997],"valid"],[[998,998],"mapped",[999]],[[999,999],"valid"],[[1000,1000],"mapped",[1001]],[[1001,1001],"valid"],[[1002,1002],"mapped",[1003]],[[1003,1003],"valid"],[[1004,1004],"mapped",[1005]],[[1005,1005],"valid"],[[1006,1006],"mapped",[1007]],[[1007,1007],"valid"],[[1008,1008],"mapped",[954]],[[1009,1009],"mapped",[961]],[[1010,1010],"mapped",[963]],[[1011,1011],"valid"],[[1012,1012],"mapped",[952]],[[1013,1013],"mapped",[949]],[[1014,1014],"valid",[],"NV8"],[[1015,1015],"mapped",[1016]],[[1016,1016],"valid"],[[1017,1017],"mapped",[963]],[[1018,1018],"mapped",[1019]],[[1019,1019],"valid"],[[1020,1020],"valid"],[[1021,1021],"mapped",[891]],[[1022,1022],"mapped",[892]],[[1023,1023],"mapped",[893]],[[1024,1024],"mapped",[1104]],[[1025,1025],"mapped",[1105]],[[1026,1026],"mapped",[1106]],[[1027,1027],"mapped",[1107]],[[1028,1028],"mapped",[1108]],[[1029,1029],"mapped",[1109]],[[1030,1030],"mapped",[1110]],[[1031,1031],"mapped",[1111]],[[1032,1032],"mapped",[1112]],[[1033,1033],"mapped",[1113]],[[1034,1034],"mapped",[1114]],[[1035,1035],"mapped",[1115]],[[1036,1036],"mapped",[1116]],[[1037,1037],"mapped",[1117]],[[1038,1038],"mapped",[1118]],[[1039,1039],"mapped",[1119]],[[1040,1040],"mapped",[1072]],[[1041,1041],"mapped",[1073]],[[1042,1042],"mapped",[1074]],[[1043,1043],"mapped",[1075]],[[1044,1044],"mapped",[1076]],[[1045,1045],"mapped",[1077]],[[1046,1046],"mapped",[1078]],[[1047,1047],"mapped",[1079]],[[1048,1048],"mapped",[1080]],[[1049,1049],"mapped",[1081]],[[1050,1050],"mapped",[1082]],[[1051,1051],"mapped",[1083]],[[1052,1052],"mapped",[1084]],[[1053,1053],"mapped",[1085]],[[1054,1054],"mapped",[1086]],[[1055,1055],"mapped",[1087]],[[1056,1056],"mapped",[1088]],[[1057,1057],"mapped",[1089]],[[1058,1058],"mapped",[1090]],[[1059,1059],"mapped",[1091]],[[1060,1060],"mapped",[1092]],[[1061,1061],"mapped",[1093]],[[1062,1062],"mapped",[1094]],[[1063,1063],"mapped",[1095]],[[1064,1064],"mapped",[1096]],[[1065,1065],"mapped",[1097]],[[1066,1066],"mapped",[1098]],[[1067,1067],"mapped",[1099]],[[1068,1068],"mapped",[1100]],[[1069,1069],"mapped",[1101]],[[1070,1070],"mapped",[1102]],[[1071,1071],"mapped",[1103]],[[1072,1103],"valid"],[[1104,1104],"valid"],[[1105,1116],"valid"],[[1117,1117],"valid"],[[1118,1119],"valid"],[[1120,1120],"mapped",[1121]],[[1121,1121],"valid"],[[1122,1122],"mapped",[1123]],[[1123,1123],"valid"],[[1124,1124],"mapped",[1125]],[[1125,1125],"valid"],[[1126,1126],"mapped",[1127]],[[1127,1127],"valid"],[[1128,1128],"mapped",[1129]],[[1129,1129],"valid"],[[1130,1130],"mapped",[1131]],[[1131,1131],"valid"],[[1132,1132],"mapped",[1133]],[[1133,1133],"valid"],[[1134,1134],"mapped",[1135]],[[1135,1135],"valid"],[[1136,1136],"mapped",[1137]],[[1137,1137],"valid"],[[1138,1138],"mapped",[1139]],[[1139,1139],"valid"],[[1140,1140],"mapped",[1141]],[[1141,1141],"valid"],[[1142,1142],"mapped",[1143]],[[1143,1143],"valid"],[[1144,1144],"mapped",[1145]],[[1145,1145],"valid"],[[1146,1146],"mapped",[1147]],[[1147,1147],"valid"],[[1148,1148],"mapped",[1149]],[[1149,1149],"valid"],[[1150,1150],"mapped",[1151]],[[1151,1151],"valid"],[[1152,1152],"mapped",[1153]],[[1153,1153],"valid"],[[1154,1154],"valid",[],"NV8"],[[1155,1158],"valid"],[[1159,1159],"valid"],[[1160,1161],"valid",[],"NV8"],[[1162,1162],"mapped",[1163]],[[1163,1163],"valid"],[[1164,1164],"mapped",[1165]],[[1165,1165],"valid"],[[1166,1166],"mapped",[1167]],[[1167,1167],"valid"],[[1168,1168],"mapped",[1169]],[[1169,1169],"valid"],[[1170,1170],"mapped",[1171]],[[1171,1171],"valid"],[[1172,1172],"mapped",[1173]],[[1173,1173],"valid"],[[1174,1174],"mapped",[1175]],[[1175,1175],"valid"],[[1176,1176],"mapped",[1177]],[[1177,1177],"valid"],[[1178,1178],"mapped",[1179]],[[1179,1179],"valid"],[[1180,1180],"mapped",[1181]],[[1181,1181],"valid"],[[1182,1182],"mapped",[1183]],[[1183,1183],"valid"],[[1184,1184],"mapped",[1185]],[[1185,1185],"valid"],[[1186,1186],"mapped",[1187]],[[1187,1187],"valid"],[[1188,1188],"mapped",[1189]],[[1189,1189],"valid"],[[1190,1190],"mapped",[1191]],[[1191,1191],"valid"],[[1192,1192],"mapped",[1193]],[[1193,1193],"valid"],[[1194,1194],"mapped",[1195]],[[1195,1195],"valid"],[[1196,1196],"mapped",[1197]],[[1197,1197],"valid"],[[1198,1198],"mapped",[1199]],[[1199,1199],"valid"],[[1200,1200],"mapped",[1201]],[[1201,1201],"valid"],[[1202,1202],"mapped",[1203]],[[1203,1203],"valid"],[[1204,1204],"mapped",[1205]],[[1205,1205],"valid"],[[1206,1206],"mapped",[1207]],[[1207,1207],"valid"],[[1208,1208],"mapped",[1209]],[[1209,1209],"valid"],[[1210,1210],"mapped",[1211]],[[1211,1211],"valid"],[[1212,1212],"mapped",[1213]],[[1213,1213],"valid"],[[1214,1214],"mapped",[1215]],[[1215,1215],"valid"],[[1216,1216],"disallowed"],[[1217,1217],"mapped",[1218]],[[1218,1218],"valid"],[[1219,1219],"mapped",[1220]],[[1220,1220],"valid"],[[1221,1221],"mapped",[1222]],[[1222,1222],"valid"],[[1223,1223],"mapped",[1224]],[[1224,1224],"valid"],[[1225,1225],"mapped",[1226]],[[1226,1226],"valid"],[[1227,1227],"mapped",[1228]],[[1228,1228],"valid"],[[1229,1229],"mapped",[1230]],[[1230,1230],"valid"],[[1231,1231],"valid"],[[1232,1232],"mapped",[1233]],[[1233,1233],"valid"],[[1234,1234],"mapped",[1235]],[[1235,1235],"valid"],[[1236,1236],"mapped",[1237]],[[1237,1237],"valid"],[[1238,1238],"mapped",[1239]],[[1239,1239],"valid"],[[1240,1240],"mapped",[1241]],[[1241,1241],"valid"],[[1242,1242],"mapped",[1243]],[[1243,1243],"valid"],[[1244,1244],"mapped",[1245]],[[1245,1245],"valid"],[[1246,1246],"mapped",[1247]],[[1247,1247],"valid"],[[1248,1248],"mapped",[1249]],[[1249,1249],"valid"],[[1250,1250],"mapped",[1251]],[[1251,1251],"valid"],[[1252,1252],"mapped",[1253]],[[1253,1253],"valid"],[[1254,1254],"mapped",[1255]],[[1255,1255],"valid"],[[1256,1256],"mapped",[1257]],[[1257,1257],"valid"],[[1258,1258],"mapped",[1259]],[[1259,1259],"valid"],[[1260,1260],"mapped",[1261]],[[1261,1261],"valid"],[[1262,1262],"mapped",[1263]],[[1263,1263],"valid"],[[1264,1264],"mapped",[1265]],[[1265,1265],"valid"],[[1266,1266],"mapped",[1267]],[[1267,1267],"valid"],[[1268,1268],"mapped",[1269]],[[1269,1269],"valid"],[[1270,1270],"mapped",[1271]],[[1271,1271],"valid"],[[1272,1272],"mapped",[1273]],[[1273,1273],"valid"],[[1274,1274],"mapped",[1275]],[[1275,1275],"valid"],[[1276,1276],"mapped",[1277]],[[1277,1277],"valid"],[[1278,1278],"mapped",[1279]],[[1279,1279],"valid"],[[1280,1280],"mapped",[1281]],[[1281,1281],"valid"],[[1282,1282],"mapped",[1283]],[[1283,1283],"valid"],[[1284,1284],"mapped",[1285]],[[1285,1285],"valid"],[[1286,1286],"mapped",[1287]],[[1287,1287],"valid"],[[1288,1288],"mapped",[1289]],[[1289,1289],"valid"],[[1290,1290],"mapped",[1291]],[[1291,1291],"valid"],[[1292,1292],"mapped",[1293]],[[1293,1293],"valid"],[[1294,1294],"mapped",[1295]],[[1295,1295],"valid"],[[1296,1296],"mapped",[1297]],[[1297,1297],"valid"],[[1298,1298],"mapped",[1299]],[[1299,1299],"valid"],[[1300,1300],"mapped",[1301]],[[1301,1301],"valid"],[[1302,1302],"mapped",[1303]],[[1303,1303],"valid"],[[1304,1304],"mapped",[1305]],[[1305,1305],"valid"],[[1306,1306],"mapped",[1307]],[[1307,1307],"valid"],[[1308,1308],"mapped",[1309]],[[1309,1309],"valid"],[[1310,1310],"mapped",[1311]],[[1311,1311],"valid"],[[1312,1312],"mapped",[1313]],[[1313,1313],"valid"],[[1314,1314],"mapped",[1315]],[[1315,1315],"valid"],[[1316,1316],"mapped",[1317]],[[1317,1317],"valid"],[[1318,1318],"mapped",[1319]],[[1319,1319],"valid"],[[1320,1320],"mapped",[1321]],[[1321,1321],"valid"],[[1322,1322],"mapped",[1323]],[[1323,1323],"valid"],[[1324,1324],"mapped",[1325]],[[1325,1325],"valid"],[[1326,1326],"mapped",[1327]],[[1327,1327],"valid"],[[1328,1328],"disallowed"],[[1329,1329],"mapped",[1377]],[[1330,1330],"mapped",[1378]],[[1331,1331],"mapped",[1379]],[[1332,1332],"mapped",[1380]],[[1333,1333],"mapped",[1381]],[[1334,1334],"mapped",[1382]],[[1335,1335],"mapped",[1383]],[[1336,1336],"mapped",[1384]],[[1337,1337],"mapped",[1385]],[[1338,1338],"mapped",[1386]],[[1339,1339],"mapped",[1387]],[[1340,1340],"mapped",[1388]],[[1341,1341],"mapped",[1389]],[[1342,1342],"mapped",[1390]],[[1343,1343],"mapped",[1391]],[[1344,1344],"mapped",[1392]],[[1345,1345],"mapped",[1393]],[[1346,1346],"mapped",[1394]],[[1347,1347],"mapped",[1395]],[[1348,1348],"mapped",[1396]],[[1349,1349],"mapped",[1397]],[[1350,1350],"mapped",[1398]],[[1351,1351],"mapped",[1399]],[[1352,1352],"mapped",[1400]],[[1353,1353],"mapped",[1401]],[[1354,1354],"mapped",[1402]],[[1355,1355],"mapped",[1403]],[[1356,1356],"mapped",[1404]],[[1357,1357],"mapped",[1405]],[[1358,1358],"mapped",[1406]],[[1359,1359],"mapped",[1407]],[[1360,1360],"mapped",[1408]],[[1361,1361],"mapped",[1409]],[[1362,1362],"mapped",[1410]],[[1363,1363],"mapped",[1411]],[[1364,1364],"mapped",[1412]],[[1365,1365],"mapped",[1413]],[[1366,1366],"mapped",[1414]],[[1367,1368],"disallowed"],[[1369,1369],"valid"],[[1370,1375],"valid",[],"NV8"],[[1376,1376],"disallowed"],[[1377,1414],"valid"],[[1415,1415],"mapped",[1381,1410]],[[1416,1416],"disallowed"],[[1417,1417],"valid",[],"NV8"],[[1418,1418],"valid",[],"NV8"],[[1419,1420],"disallowed"],[[1421,1422],"valid",[],"NV8"],[[1423,1423],"valid",[],"NV8"],[[1424,1424],"disallowed"],[[1425,1441],"valid"],[[1442,1442],"valid"],[[1443,1455],"valid"],[[1456,1465],"valid"],[[1466,1466],"valid"],[[1467,1469],"valid"],[[1470,1470],"valid",[],"NV8"],[[1471,1471],"valid"],[[1472,1472],"valid",[],"NV8"],[[1473,1474],"valid"],[[1475,1475],"valid",[],"NV8"],[[1476,1476],"valid"],[[1477,1477],"valid"],[[1478,1478],"valid",[],"NV8"],[[1479,1479],"valid"],[[1480,1487],"disallowed"],[[1488,1514],"valid"],[[1515,1519],"disallowed"],[[1520,1524],"valid"],[[1525,1535],"disallowed"],[[1536,1539],"disallowed"],[[1540,1540],"disallowed"],[[1541,1541],"disallowed"],[[1542,1546],"valid",[],"NV8"],[[1547,1547],"valid",[],"NV8"],[[1548,1548],"valid",[],"NV8"],[[1549,1551],"valid",[],"NV8"],[[1552,1557],"valid"],[[1558,1562],"valid"],[[1563,1563],"valid",[],"NV8"],[[1564,1564],"disallowed"],[[1565,1565],"disallowed"],[[1566,1566],"valid",[],"NV8"],[[1567,1567],"valid",[],"NV8"],[[1568,1568],"valid"],[[1569,1594],"valid"],[[1595,1599],"valid"],[[1600,1600],"valid",[],"NV8"],[[1601,1618],"valid"],[[1619,1621],"valid"],[[1622,1624],"valid"],[[1625,1630],"valid"],[[1631,1631],"valid"],[[1632,1641],"valid"],[[1642,1645],"valid",[],"NV8"],[[1646,1647],"valid"],[[1648,1652],"valid"],[[1653,1653],"mapped",[1575,1652]],[[1654,1654],"mapped",[1608,1652]],[[1655,1655],"mapped",[1735,1652]],[[1656,1656],"mapped",[1610,1652]],[[1657,1719],"valid"],[[1720,1721],"valid"],[[1722,1726],"valid"],[[1727,1727],"valid"],[[1728,1742],"valid"],[[1743,1743],"valid"],[[1744,1747],"valid"],[[1748,1748],"valid",[],"NV8"],[[1749,1756],"valid"],[[1757,1757],"disallowed"],[[1758,1758],"valid",[],"NV8"],[[1759,1768],"valid"],[[1769,1769],"valid",[],"NV8"],[[1770,1773],"valid"],[[1774,1775],"valid"],[[1776,1785],"valid"],[[1786,1790],"valid"],[[1791,1791],"valid"],[[1792,1805],"valid",[],"NV8"],[[1806,1806],"disallowed"],[[1807,1807],"disallowed"],[[1808,1836],"valid"],[[1837,1839],"valid"],[[1840,1866],"valid"],[[1867,1868],"disallowed"],[[1869,1871],"valid"],[[1872,1901],"valid"],[[1902,1919],"valid"],[[1920,1968],"valid"],[[1969,1969],"valid"],[[1970,1983],"disallowed"],[[1984,2037],"valid"],[[2038,2042],"valid",[],"NV8"],[[2043,2047],"disallowed"],[[2048,2093],"valid"],[[2094,2095],"disallowed"],[[2096,2110],"valid",[],"NV8"],[[2111,2111],"disallowed"],[[2112,2139],"valid"],[[2140,2141],"disallowed"],[[2142,2142],"valid",[],"NV8"],[[2143,2207],"disallowed"],[[2208,2208],"valid"],[[2209,2209],"valid"],[[2210,2220],"valid"],[[2221,2226],"valid"],[[2227,2228],"valid"],[[2229,2274],"disallowed"],[[2275,2275],"valid"],[[2276,2302],"valid"],[[2303,2303],"valid"],[[2304,2304],"valid"],[[2305,2307],"valid"],[[2308,2308],"valid"],[[2309,2361],"valid"],[[2362,2363],"valid"],[[2364,2381],"valid"],[[2382,2382],"valid"],[[2383,2383],"valid"],[[2384,2388],"valid"],[[2389,2389],"valid"],[[2390,2391],"valid"],[[2392,2392],"mapped",[2325,2364]],[[2393,2393],"mapped",[2326,2364]],[[2394,2394],"mapped",[2327,2364]],[[2395,2395],"mapped",[2332,2364]],[[2396,2396],"mapped",[2337,2364]],[[2397,2397],"mapped",[2338,2364]],[[2398,2398],"mapped",[2347,2364]],[[2399,2399],"mapped",[2351,2364]],[[2400,2403],"valid"],[[2404,2405],"valid",[],"NV8"],[[2406,2415],"valid"],[[2416,2416],"valid",[],"NV8"],[[2417,2418],"valid"],[[2419,2423],"valid"],[[2424,2424],"valid"],[[2425,2426],"valid"],[[2427,2428],"valid"],[[2429,2429],"valid"],[[2430,2431],"valid"],[[2432,2432],"valid"],[[2433,2435],"valid"],[[2436,2436],"disallowed"],[[2437,2444],"valid"],[[2445,2446],"disallowed"],[[2447,2448],"valid"],[[2449,2450],"disallowed"],[[2451,2472],"valid"],[[2473,2473],"disallowed"],[[2474,2480],"valid"],[[2481,2481],"disallowed"],[[2482,2482],"valid"],[[2483,2485],"disallowed"],[[2486,2489],"valid"],[[2490,2491],"disallowed"],[[2492,2492],"valid"],[[2493,2493],"valid"],[[2494,2500],"valid"],[[2501,2502],"disallowed"],[[2503,2504],"valid"],[[2505,2506],"disallowed"],[[2507,2509],"valid"],[[2510,2510],"valid"],[[2511,2518],"disallowed"],[[2519,2519],"valid"],[[2520,2523],"disallowed"],[[2524,2524],"mapped",[2465,2492]],[[2525,2525],"mapped",[2466,2492]],[[2526,2526],"disallowed"],[[2527,2527],"mapped",[2479,2492]],[[2528,2531],"valid"],[[2532,2533],"disallowed"],[[2534,2545],"valid"],[[2546,2554],"valid",[],"NV8"],[[2555,2555],"valid",[],"NV8"],[[2556,2560],"disallowed"],[[2561,2561],"valid"],[[2562,2562],"valid"],[[2563,2563],"valid"],[[2564,2564],"disallowed"],[[2565,2570],"valid"],[[2571,2574],"disallowed"],[[2575,2576],"valid"],[[2577,2578],"disallowed"],[[2579,2600],"valid"],[[2601,2601],"disallowed"],[[2602,2608],"valid"],[[2609,2609],"disallowed"],[[2610,2610],"valid"],[[2611,2611],"mapped",[2610,2620]],[[2612,2612],"disallowed"],[[2613,2613],"valid"],[[2614,2614],"mapped",[2616,2620]],[[2615,2615],"disallowed"],[[2616,2617],"valid"],[[2618,2619],"disallowed"],[[2620,2620],"valid"],[[2621,2621],"disallowed"],[[2622,2626],"valid"],[[2627,2630],"disallowed"],[[2631,2632],"valid"],[[2633,2634],"disallowed"],[[2635,2637],"valid"],[[2638,2640],"disallowed"],[[2641,2641],"valid"],[[2642,2648],"disallowed"],[[2649,2649],"mapped",[2582,2620]],[[2650,2650],"mapped",[2583,2620]],[[2651,2651],"mapped",[2588,2620]],[[2652,2652],"valid"],[[2653,2653],"disallowed"],[[2654,2654],"mapped",[2603,2620]],[[2655,2661],"disallowed"],[[2662,2676],"valid"],[[2677,2677],"valid"],[[2678,2688],"disallowed"],[[2689,2691],"valid"],[[2692,2692],"disallowed"],[[2693,2699],"valid"],[[2700,2700],"valid"],[[2701,2701],"valid"],[[2702,2702],"disallowed"],[[2703,2705],"valid"],[[2706,2706],"disallowed"],[[2707,2728],"valid"],[[2729,2729],"disallowed"],[[2730,2736],"valid"],[[2737,2737],"disallowed"],[[2738,2739],"valid"],[[2740,2740],"disallowed"],[[2741,2745],"valid"],[[2746,2747],"disallowed"],[[2748,2757],"valid"],[[2758,2758],"disallowed"],[[2759,2761],"valid"],[[2762,2762],"disallowed"],[[2763,2765],"valid"],[[2766,2767],"disallowed"],[[2768,2768],"valid"],[[2769,2783],"disallowed"],[[2784,2784],"valid"],[[2785,2787],"valid"],[[2788,2789],"disallowed"],[[2790,2799],"valid"],[[2800,2800],"valid",[],"NV8"],[[2801,2801],"valid",[],"NV8"],[[2802,2808],"disallowed"],[[2809,2809],"valid"],[[2810,2816],"disallowed"],[[2817,2819],"valid"],[[2820,2820],"disallowed"],[[2821,2828],"valid"],[[2829,2830],"disallowed"],[[2831,2832],"valid"],[[2833,2834],"disallowed"],[[2835,2856],"valid"],[[2857,2857],"disallowed"],[[2858,2864],"valid"],[[2865,2865],"disallowed"],[[2866,2867],"valid"],[[2868,2868],"disallowed"],[[2869,2869],"valid"],[[2870,2873],"valid"],[[2874,2875],"disallowed"],[[2876,2883],"valid"],[[2884,2884],"valid"],[[2885,2886],"disallowed"],[[2887,2888],"valid"],[[2889,2890],"disallowed"],[[2891,2893],"valid"],[[2894,2901],"disallowed"],[[2902,2903],"valid"],[[2904,2907],"disallowed"],[[2908,2908],"mapped",[2849,2876]],[[2909,2909],"mapped",[2850,2876]],[[2910,2910],"disallowed"],[[2911,2913],"valid"],[[2914,2915],"valid"],[[2916,2917],"disallowed"],[[2918,2927],"valid"],[[2928,2928],"valid",[],"NV8"],[[2929,2929],"valid"],[[2930,2935],"valid",[],"NV8"],[[2936,2945],"disallowed"],[[2946,2947],"valid"],[[2948,2948],"disallowed"],[[2949,2954],"valid"],[[2955,2957],"disallowed"],[[2958,2960],"valid"],[[2961,2961],"disallowed"],[[2962,2965],"valid"],[[2966,2968],"disallowed"],[[2969,2970],"valid"],[[2971,2971],"disallowed"],[[2972,2972],"valid"],[[2973,2973],"disallowed"],[[2974,2975],"valid"],[[2976,2978],"disallowed"],[[2979,2980],"valid"],[[2981,2983],"disallowed"],[[2984,2986],"valid"],[[2987,2989],"disallowed"],[[2990,2997],"valid"],[[2998,2998],"valid"],[[2999,3001],"valid"],[[3002,3005],"disallowed"],[[3006,3010],"valid"],[[3011,3013],"disallowed"],[[3014,3016],"valid"],[[3017,3017],"disallowed"],[[3018,3021],"valid"],[[3022,3023],"disallowed"],[[3024,3024],"valid"],[[3025,3030],"disallowed"],[[3031,3031],"valid"],[[3032,3045],"disallowed"],[[3046,3046],"valid"],[[3047,3055],"valid"],[[3056,3058],"valid",[],"NV8"],[[3059,3066],"valid",[],"NV8"],[[3067,3071],"disallowed"],[[3072,3072],"valid"],[[3073,3075],"valid"],[[3076,3076],"disallowed"],[[3077,3084],"valid"],[[3085,3085],"disallowed"],[[3086,3088],"valid"],[[3089,3089],"disallowed"],[[3090,3112],"valid"],[[3113,3113],"disallowed"],[[3114,3123],"valid"],[[3124,3124],"valid"],[[3125,3129],"valid"],[[3130,3132],"disallowed"],[[3133,3133],"valid"],[[3134,3140],"valid"],[[3141,3141],"disallowed"],[[3142,3144],"valid"],[[3145,3145],"disallowed"],[[3146,3149],"valid"],[[3150,3156],"disallowed"],[[3157,3158],"valid"],[[3159,3159],"disallowed"],[[3160,3161],"valid"],[[3162,3162],"valid"],[[3163,3167],"disallowed"],[[3168,3169],"valid"],[[3170,3171],"valid"],[[3172,3173],"disallowed"],[[3174,3183],"valid"],[[3184,3191],"disallowed"],[[3192,3199],"valid",[],"NV8"],[[3200,3200],"disallowed"],[[3201,3201],"valid"],[[3202,3203],"valid"],[[3204,3204],"disallowed"],[[3205,3212],"valid"],[[3213,3213],"disallowed"],[[3214,3216],"valid"],[[3217,3217],"disallowed"],[[3218,3240],"valid"],[[3241,3241],"disallowed"],[[3242,3251],"valid"],[[3252,3252],"disallowed"],[[3253,3257],"valid"],[[3258,3259],"disallowed"],[[3260,3261],"valid"],[[3262,3268],"valid"],[[3269,3269],"disallowed"],[[3270,3272],"valid"],[[3273,3273],"disallowed"],[[3274,3277],"valid"],[[3278,3284],"disallowed"],[[3285,3286],"valid"],[[3287,3293],"disallowed"],[[3294,3294],"valid"],[[3295,3295],"disallowed"],[[3296,3297],"valid"],[[3298,3299],"valid"],[[3300,3301],"disallowed"],[[3302,3311],"valid"],[[3312,3312],"disallowed"],[[3313,3314],"valid"],[[3315,3328],"disallowed"],[[3329,3329],"valid"],[[3330,3331],"valid"],[[3332,3332],"disallowed"],[[3333,3340],"valid"],[[3341,3341],"disallowed"],[[3342,3344],"valid"],[[3345,3345],"disallowed"],[[3346,3368],"valid"],[[3369,3369],"valid"],[[3370,3385],"valid"],[[3386,3386],"valid"],[[3387,3388],"disallowed"],[[3389,3389],"valid"],[[3390,3395],"valid"],[[3396,3396],"valid"],[[3397,3397],"disallowed"],[[3398,3400],"valid"],[[3401,3401],"disallowed"],[[3402,3405],"valid"],[[3406,3406],"valid"],[[3407,3414],"disallowed"],[[3415,3415],"valid"],[[3416,3422],"disallowed"],[[3423,3423],"valid"],[[3424,3425],"valid"],[[3426,3427],"valid"],[[3428,3429],"disallowed"],[[3430,3439],"valid"],[[3440,3445],"valid",[],"NV8"],[[3446,3448],"disallowed"],[[3449,3449],"valid",[],"NV8"],[[3450,3455],"valid"],[[3456,3457],"disallowed"],[[3458,3459],"valid"],[[3460,3460],"disallowed"],[[3461,3478],"valid"],[[3479,3481],"disallowed"],[[3482,3505],"valid"],[[3506,3506],"disallowed"],[[3507,3515],"valid"],[[3516,3516],"disallowed"],[[3517,3517],"valid"],[[3518,3519],"disallowed"],[[3520,3526],"valid"],[[3527,3529],"disallowed"],[[3530,3530],"valid"],[[3531,3534],"disallowed"],[[3535,3540],"valid"],[[3541,3541],"disallowed"],[[3542,3542],"valid"],[[3543,3543],"disallowed"],[[3544,3551],"valid"],[[3552,3557],"disallowed"],[[3558,3567],"valid"],[[3568,3569],"disallowed"],[[3570,3571],"valid"],[[3572,3572],"valid",[],"NV8"],[[3573,3584],"disallowed"],[[3585,3634],"valid"],[[3635,3635],"mapped",[3661,3634]],[[3636,3642],"valid"],[[3643,3646],"disallowed"],[[3647,3647],"valid",[],"NV8"],[[3648,3662],"valid"],[[3663,3663],"valid",[],"NV8"],[[3664,3673],"valid"],[[3674,3675],"valid",[],"NV8"],[[3676,3712],"disallowed"],[[3713,3714],"valid"],[[3715,3715],"disallowed"],[[3716,3716],"valid"],[[3717,3718],"disallowed"],[[3719,3720],"valid"],[[3721,3721],"disallowed"],[[3722,3722],"valid"],[[3723,3724],"disallowed"],[[3725,3725],"valid"],[[3726,3731],"disallowed"],[[3732,3735],"valid"],[[3736,3736],"disallowed"],[[3737,3743],"valid"],[[3744,3744],"disallowed"],[[3745,3747],"valid"],[[3748,3748],"disallowed"],[[3749,3749],"valid"],[[3750,3750],"disallowed"],[[3751,3751],"valid"],[[3752,3753],"disallowed"],[[3754,3755],"valid"],[[3756,3756],"disallowed"],[[3757,3762],"valid"],[[3763,3763],"mapped",[3789,3762]],[[3764,3769],"valid"],[[3770,3770],"disallowed"],[[3771,3773],"valid"],[[3774,3775],"disallowed"],[[3776,3780],"valid"],[[3781,3781],"disallowed"],[[3782,3782],"valid"],[[3783,3783],"disallowed"],[[3784,3789],"valid"],[[3790,3791],"disallowed"],[[3792,3801],"valid"],[[3802,3803],"disallowed"],[[3804,3804],"mapped",[3755,3737]],[[3805,3805],"mapped",[3755,3745]],[[3806,3807],"valid"],[[3808,3839],"disallowed"],[[3840,3840],"valid"],[[3841,3850],"valid",[],"NV8"],[[3851,3851],"valid"],[[3852,3852],"mapped",[3851]],[[3853,3863],"valid",[],"NV8"],[[3864,3865],"valid"],[[3866,3871],"valid",[],"NV8"],[[3872,3881],"valid"],[[3882,3892],"valid",[],"NV8"],[[3893,3893],"valid"],[[3894,3894],"valid",[],"NV8"],[[3895,3895],"valid"],[[3896,3896],"valid",[],"NV8"],[[3897,3897],"valid"],[[3898,3901],"valid",[],"NV8"],[[3902,3906],"valid"],[[3907,3907],"mapped",[3906,4023]],[[3908,3911],"valid"],[[3912,3912],"disallowed"],[[3913,3916],"valid"],[[3917,3917],"mapped",[3916,4023]],[[3918,3921],"valid"],[[3922,3922],"mapped",[3921,4023]],[[3923,3926],"valid"],[[3927,3927],"mapped",[3926,4023]],[[3928,3931],"valid"],[[3932,3932],"mapped",[3931,4023]],[[3933,3944],"valid"],[[3945,3945],"mapped",[3904,4021]],[[3946,3946],"valid"],[[3947,3948],"valid"],[[3949,3952],"disallowed"],[[3953,3954],"valid"],[[3955,3955],"mapped",[3953,3954]],[[3956,3956],"valid"],[[3957,3957],"mapped",[3953,3956]],[[3958,3958],"mapped",[4018,3968]],[[3959,3959],"mapped",[4018,3953,3968]],[[3960,3960],"mapped",[4019,3968]],[[3961,3961],"mapped",[4019,3953,3968]],[[3962,3968],"valid"],[[3969,3969],"mapped",[3953,3968]],[[3970,3972],"valid"],[[3973,3973],"valid",[],"NV8"],[[3974,3979],"valid"],[[3980,3983],"valid"],[[3984,3986],"valid"],[[3987,3987],"mapped",[3986,4023]],[[3988,3989],"valid"],[[3990,3990],"valid"],[[3991,3991],"valid"],[[3992,3992],"disallowed"],[[3993,3996],"valid"],[[3997,3997],"mapped",[3996,4023]],[[3998,4001],"valid"],[[4002,4002],"mapped",[4001,4023]],[[4003,4006],"valid"],[[4007,4007],"mapped",[4006,4023]],[[4008,4011],"valid"],[[4012,4012],"mapped",[4011,4023]],[[4013,4013],"valid"],[[4014,4016],"valid"],[[4017,4023],"valid"],[[4024,4024],"valid"],[[4025,4025],"mapped",[3984,4021]],[[4026,4028],"valid"],[[4029,4029],"disallowed"],[[4030,4037],"valid",[],"NV8"],[[4038,4038],"valid"],[[4039,4044],"valid",[],"NV8"],[[4045,4045],"disallowed"],[[4046,4046],"valid",[],"NV8"],[[4047,4047],"valid",[],"NV8"],[[4048,4049],"valid",[],"NV8"],[[4050,4052],"valid",[],"NV8"],[[4053,4056],"valid",[],"NV8"],[[4057,4058],"valid",[],"NV8"],[[4059,4095],"disallowed"],[[4096,4129],"valid"],[[4130,4130],"valid"],[[4131,4135],"valid"],[[4136,4136],"valid"],[[4137,4138],"valid"],[[4139,4139],"valid"],[[4140,4146],"valid"],[[4147,4149],"valid"],[[4150,4153],"valid"],[[4154,4159],"valid"],[[4160,4169],"valid"],[[4170,4175],"valid",[],"NV8"],[[4176,4185],"valid"],[[4186,4249],"valid"],[[4250,4253],"valid"],[[4254,4255],"valid",[],"NV8"],[[4256,4293],"disallowed"],[[4294,4294],"disallowed"],[[4295,4295],"mapped",[11559]],[[4296,4300],"disallowed"],[[4301,4301],"mapped",[11565]],[[4302,4303],"disallowed"],[[4304,4342],"valid"],[[4343,4344],"valid"],[[4345,4346],"valid"],[[4347,4347],"valid",[],"NV8"],[[4348,4348],"mapped",[4316]],[[4349,4351],"valid"],[[4352,4441],"valid",[],"NV8"],[[4442,4446],"valid",[],"NV8"],[[4447,4448],"disallowed"],[[4449,4514],"valid",[],"NV8"],[[4515,4519],"valid",[],"NV8"],[[4520,4601],"valid",[],"NV8"],[[4602,4607],"valid",[],"NV8"],[[4608,4614],"valid"],[[4615,4615],"valid"],[[4616,4678],"valid"],[[4679,4679],"valid"],[[4680,4680],"valid"],[[4681,4681],"disallowed"],[[4682,4685],"valid"],[[4686,4687],"disallowed"],[[4688,4694],"valid"],[[4695,4695],"disallowed"],[[4696,4696],"valid"],[[4697,4697],"disallowed"],[[4698,4701],"valid"],[[4702,4703],"disallowed"],[[4704,4742],"valid"],[[4743,4743],"valid"],[[4744,4744],"valid"],[[4745,4745],"disallowed"],[[4746,4749],"valid"],[[4750,4751],"disallowed"],[[4752,4782],"valid"],[[4783,4783],"valid"],[[4784,4784],"valid"],[[4785,4785],"disallowed"],[[4786,4789],"valid"],[[4790,4791],"disallowed"],[[4792,4798],"valid"],[[4799,4799],"disallowed"],[[4800,4800],"valid"],[[4801,4801],"disallowed"],[[4802,4805],"valid"],[[4806,4807],"disallowed"],[[4808,4814],"valid"],[[4815,4815],"valid"],[[4816,4822],"valid"],[[4823,4823],"disallowed"],[[4824,4846],"valid"],[[4847,4847],"valid"],[[4848,4878],"valid"],[[4879,4879],"valid"],[[4880,4880],"valid"],[[4881,4881],"disallowed"],[[4882,4885],"valid"],[[4886,4887],"disallowed"],[[4888,4894],"valid"],[[4895,4895],"valid"],[[4896,4934],"valid"],[[4935,4935],"valid"],[[4936,4954],"valid"],[[4955,4956],"disallowed"],[[4957,4958],"valid"],[[4959,4959],"valid"],[[4960,4960],"valid",[],"NV8"],[[4961,4988],"valid",[],"NV8"],[[4989,4991],"disallowed"],[[4992,5007],"valid"],[[5008,5017],"valid",[],"NV8"],[[5018,5023],"disallowed"],[[5024,5108],"valid"],[[5109,5109],"valid"],[[5110,5111],"disallowed"],[[5112,5112],"mapped",[5104]],[[5113,5113],"mapped",[5105]],[[5114,5114],"mapped",[5106]],[[5115,5115],"mapped",[5107]],[[5116,5116],"mapped",[5108]],[[5117,5117],"mapped",[5109]],[[5118,5119],"disallowed"],[[5120,5120],"valid",[],"NV8"],[[5121,5740],"valid"],[[5741,5742],"valid",[],"NV8"],[[5743,5750],"valid"],[[5751,5759],"valid"],[[5760,5760],"disallowed"],[[5761,5786],"valid"],[[5787,5788],"valid",[],"NV8"],[[5789,5791],"disallowed"],[[5792,5866],"valid"],[[5867,5872],"valid",[],"NV8"],[[5873,5880],"valid"],[[5881,5887],"disallowed"],[[5888,5900],"valid"],[[5901,5901],"disallowed"],[[5902,5908],"valid"],[[5909,5919],"disallowed"],[[5920,5940],"valid"],[[5941,5942],"valid",[],"NV8"],[[5943,5951],"disallowed"],[[5952,5971],"valid"],[[5972,5983],"disallowed"],[[5984,5996],"valid"],[[5997,5997],"disallowed"],[[5998,6000],"valid"],[[6001,6001],"disallowed"],[[6002,6003],"valid"],[[6004,6015],"disallowed"],[[6016,6067],"valid"],[[6068,6069],"disallowed"],[[6070,6099],"valid"],[[6100,6102],"valid",[],"NV8"],[[6103,6103],"valid"],[[6104,6107],"valid",[],"NV8"],[[6108,6108],"valid"],[[6109,6109],"valid"],[[6110,6111],"disallowed"],[[6112,6121],"valid"],[[6122,6127],"disallowed"],[[6128,6137],"valid",[],"NV8"],[[6138,6143],"disallowed"],[[6144,6149],"valid",[],"NV8"],[[6150,6150],"disallowed"],[[6151,6154],"valid",[],"NV8"],[[6155,6157],"ignored"],[[6158,6158],"disallowed"],[[6159,6159],"disallowed"],[[6160,6169],"valid"],[[6170,6175],"disallowed"],[[6176,6263],"valid"],[[6264,6271],"disallowed"],[[6272,6313],"valid"],[[6314,6314],"valid"],[[6315,6319],"disallowed"],[[6320,6389],"valid"],[[6390,6399],"disallowed"],[[6400,6428],"valid"],[[6429,6430],"valid"],[[6431,6431],"disallowed"],[[6432,6443],"valid"],[[6444,6447],"disallowed"],[[6448,6459],"valid"],[[6460,6463],"disallowed"],[[6464,6464],"valid",[],"NV8"],[[6465,6467],"disallowed"],[[6468,6469],"valid",[],"NV8"],[[6470,6509],"valid"],[[6510,6511],"disallowed"],[[6512,6516],"valid"],[[6517,6527],"disallowed"],[[6528,6569],"valid"],[[6570,6571],"valid"],[[6572,6575],"disallowed"],[[6576,6601],"valid"],[[6602,6607],"disallowed"],[[6608,6617],"valid"],[[6618,6618],"valid",[],"XV8"],[[6619,6621],"disallowed"],[[6622,6623],"valid",[],"NV8"],[[6624,6655],"valid",[],"NV8"],[[6656,6683],"valid"],[[6684,6685],"disallowed"],[[6686,6687],"valid",[],"NV8"],[[6688,6750],"valid"],[[6751,6751],"disallowed"],[[6752,6780],"valid"],[[6781,6782],"disallowed"],[[6783,6793],"valid"],[[6794,6799],"disallowed"],[[6800,6809],"valid"],[[6810,6815],"disallowed"],[[6816,6822],"valid",[],"NV8"],[[6823,6823],"valid"],[[6824,6829],"valid",[],"NV8"],[[6830,6831],"disallowed"],[[6832,6845],"valid"],[[6846,6846],"valid",[],"NV8"],[[6847,6911],"disallowed"],[[6912,6987],"valid"],[[6988,6991],"disallowed"],[[6992,7001],"valid"],[[7002,7018],"valid",[],"NV8"],[[7019,7027],"valid"],[[7028,7036],"valid",[],"NV8"],[[7037,7039],"disallowed"],[[7040,7082],"valid"],[[7083,7085],"valid"],[[7086,7097],"valid"],[[7098,7103],"valid"],[[7104,7155],"valid"],[[7156,7163],"disallowed"],[[7164,7167],"valid",[],"NV8"],[[7168,7223],"valid"],[[7224,7226],"disallowed"],[[7227,7231],"valid",[],"NV8"],[[7232,7241],"valid"],[[7242,7244],"disallowed"],[[7245,7293],"valid"],[[7294,7295],"valid",[],"NV8"],[[7296,7359],"disallowed"],[[7360,7367],"valid",[],"NV8"],[[7368,7375],"disallowed"],[[7376,7378],"valid"],[[7379,7379],"valid",[],"NV8"],[[7380,7410],"valid"],[[7411,7414],"valid"],[[7415,7415],"disallowed"],[[7416,7417],"valid"],[[7418,7423],"disallowed"],[[7424,7467],"valid"],[[7468,7468],"mapped",[97]],[[7469,7469],"mapped",[230]],[[7470,7470],"mapped",[98]],[[7471,7471],"valid"],[[7472,7472],"mapped",[100]],[[7473,7473],"mapped",[101]],[[7474,7474],"mapped",[477]],[[7475,7475],"mapped",[103]],[[7476,7476],"mapped",[104]],[[7477,7477],"mapped",[105]],[[7478,7478],"mapped",[106]],[[7479,7479],"mapped",[107]],[[7480,7480],"mapped",[108]],[[7481,7481],"mapped",[109]],[[7482,7482],"mapped",[110]],[[7483,7483],"valid"],[[7484,7484],"mapped",[111]],[[7485,7485],"mapped",[547]],[[7486,7486],"mapped",[112]],[[7487,7487],"mapped",[114]],[[7488,7488],"mapped",[116]],[[7489,7489],"mapped",[117]],[[7490,7490],"mapped",[119]],[[7491,7491],"mapped",[97]],[[7492,7492],"mapped",[592]],[[7493,7493],"mapped",[593]],[[7494,7494],"mapped",[7426]],[[7495,7495],"mapped",[98]],[[7496,7496],"mapped",[100]],[[7497,7497],"mapped",[101]],[[7498,7498],"mapped",[601]],[[7499,7499],"mapped",[603]],[[7500,7500],"mapped",[604]],[[7501,7501],"mapped",[103]],[[7502,7502],"valid"],[[7503,7503],"mapped",[107]],[[7504,7504],"mapped",[109]],[[7505,7505],"mapped",[331]],[[7506,7506],"mapped",[111]],[[7507,7507],"mapped",[596]],[[7508,7508],"mapped",[7446]],[[7509,7509],"mapped",[7447]],[[7510,7510],"mapped",[112]],[[7511,7511],"mapped",[116]],[[7512,7512],"mapped",[117]],[[7513,7513],"mapped",[7453]],[[7514,7514],"mapped",[623]],[[7515,7515],"mapped",[118]],[[7516,7516],"mapped",[7461]],[[7517,7517],"mapped",[946]],[[7518,7518],"mapped",[947]],[[7519,7519],"mapped",[948]],[[7520,7520],"mapped",[966]],[[7521,7521],"mapped",[967]],[[7522,7522],"mapped",[105]],[[7523,7523],"mapped",[114]],[[7524,7524],"mapped",[117]],[[7525,7525],"mapped",[118]],[[7526,7526],"mapped",[946]],[[7527,7527],"mapped",[947]],[[7528,7528],"mapped",[961]],[[7529,7529],"mapped",[966]],[[7530,7530],"mapped",[967]],[[7531,7531],"valid"],[[7532,7543],"valid"],[[7544,7544],"mapped",[1085]],[[7545,7578],"valid"],[[7579,7579],"mapped",[594]],[[7580,7580],"mapped",[99]],[[7581,7581],"mapped",[597]],[[7582,7582],"mapped",[240]],[[7583,7583],"mapped",[604]],[[7584,7584],"mapped",[102]],[[7585,7585],"mapped",[607]],[[7586,7586],"mapped",[609]],[[7587,7587],"mapped",[613]],[[7588,7588],"mapped",[616]],[[7589,7589],"mapped",[617]],[[7590,7590],"mapped",[618]],[[7591,7591],"mapped",[7547]],[[7592,7592],"mapped",[669]],[[7593,7593],"mapped",[621]],[[7594,7594],"mapped",[7557]],[[7595,7595],"mapped",[671]],[[7596,7596],"mapped",[625]],[[7597,7597],"mapped",[624]],[[7598,7598],"mapped",[626]],[[7599,7599],"mapped",[627]],[[7600,7600],"mapped",[628]],[[7601,7601],"mapped",[629]],[[7602,7602],"mapped",[632]],[[7603,7603],"mapped",[642]],[[7604,7604],"mapped",[643]],[[7605,7605],"mapped",[427]],[[7606,7606],"mapped",[649]],[[7607,7607],"mapped",[650]],[[7608,7608],"mapped",[7452]],[[7609,7609],"mapped",[651]],[[7610,7610],"mapped",[652]],[[7611,7611],"mapped",[122]],[[7612,7612],"mapped",[656]],[[7613,7613],"mapped",[657]],[[7614,7614],"mapped",[658]],[[7615,7615],"mapped",[952]],[[7616,7619],"valid"],[[7620,7626],"valid"],[[7627,7654],"valid"],[[7655,7669],"valid"],[[7670,7675],"disallowed"],[[7676,7676],"valid"],[[7677,7677],"valid"],[[7678,7679],"valid"],[[7680,7680],"mapped",[7681]],[[7681,7681],"valid"],[[7682,7682],"mapped",[7683]],[[7683,7683],"valid"],[[7684,7684],"mapped",[7685]],[[7685,7685],"valid"],[[7686,7686],"mapped",[7687]],[[7687,7687],"valid"],[[7688,7688],"mapped",[7689]],[[7689,7689],"valid"],[[7690,7690],"mapped",[7691]],[[7691,7691],"valid"],[[7692,7692],"mapped",[7693]],[[7693,7693],"valid"],[[7694,7694],"mapped",[7695]],[[7695,7695],"valid"],[[7696,7696],"mapped",[7697]],[[7697,7697],"valid"],[[7698,7698],"mapped",[7699]],[[7699,7699],"valid"],[[7700,7700],"mapped",[7701]],[[7701,7701],"valid"],[[7702,7702],"mapped",[7703]],[[7703,7703],"valid"],[[7704,7704],"mapped",[7705]],[[7705,7705],"valid"],[[7706,7706],"mapped",[7707]],[[7707,7707],"valid"],[[7708,7708],"mapped",[7709]],[[7709,7709],"valid"],[[7710,7710],"mapped",[7711]],[[7711,7711],"valid"],[[7712,7712],"mapped",[7713]],[[7713,7713],"valid"],[[7714,7714],"mapped",[7715]],[[7715,7715],"valid"],[[7716,7716],"mapped",[7717]],[[7717,7717],"valid"],[[7718,7718],"mapped",[7719]],[[7719,7719],"valid"],[[7720,7720],"mapped",[7721]],[[7721,7721],"valid"],[[7722,7722],"mapped",[7723]],[[7723,7723],"valid"],[[7724,7724],"mapped",[7725]],[[7725,7725],"valid"],[[7726,7726],"mapped",[7727]],[[7727,7727],"valid"],[[7728,7728],"mapped",[7729]],[[7729,7729],"valid"],[[7730,7730],"mapped",[7731]],[[7731,7731],"valid"],[[7732,7732],"mapped",[7733]],[[7733,7733],"valid"],[[7734,7734],"mapped",[7735]],[[7735,7735],"valid"],[[7736,7736],"mapped",[7737]],[[7737,7737],"valid"],[[7738,7738],"mapped",[7739]],[[7739,7739],"valid"],[[7740,7740],"mapped",[7741]],[[7741,7741],"valid"],[[7742,7742],"mapped",[7743]],[[7743,7743],"valid"],[[7744,7744],"mapped",[7745]],[[7745,7745],"valid"],[[7746,7746],"mapped",[7747]],[[7747,7747],"valid"],[[7748,7748],"mapped",[7749]],[[7749,7749],"valid"],[[7750,7750],"mapped",[7751]],[[7751,7751],"valid"],[[7752,7752],"mapped",[7753]],[[7753,7753],"valid"],[[7754,7754],"mapped",[7755]],[[7755,7755],"valid"],[[7756,7756],"mapped",[7757]],[[7757,7757],"valid"],[[7758,7758],"mapped",[7759]],[[7759,7759],"valid"],[[7760,7760],"mapped",[7761]],[[7761,7761],"valid"],[[7762,7762],"mapped",[7763]],[[7763,7763],"valid"],[[7764,7764],"mapped",[7765]],[[7765,7765],"valid"],[[7766,7766],"mapped",[7767]],[[7767,7767],"valid"],[[7768,7768],"mapped",[7769]],[[7769,7769],"valid"],[[7770,7770],"mapped",[7771]],[[7771,7771],"valid"],[[7772,7772],"mapped",[7773]],[[7773,7773],"valid"],[[7774,7774],"mapped",[7775]],[[7775,7775],"valid"],[[7776,7776],"mapped",[7777]],[[7777,7777],"valid"],[[7778,7778],"mapped",[7779]],[[7779,7779],"valid"],[[7780,7780],"mapped",[7781]],[[7781,7781],"valid"],[[7782,7782],"mapped",[7783]],[[7783,7783],"valid"],[[7784,7784],"mapped",[7785]],[[7785,7785],"valid"],[[7786,7786],"mapped",[7787]],[[7787,7787],"valid"],[[7788,7788],"mapped",[7789]],[[7789,7789],"valid"],[[7790,7790],"mapped",[7791]],[[7791,7791],"valid"],[[7792,7792],"mapped",[7793]],[[7793,7793],"valid"],[[7794,7794],"mapped",[7795]],[[7795,7795],"valid"],[[7796,7796],"mapped",[7797]],[[7797,7797],"valid"],[[7798,7798],"mapped",[7799]],[[7799,7799],"valid"],[[7800,7800],"mapped",[7801]],[[7801,7801],"valid"],[[7802,7802],"mapped",[7803]],[[7803,7803],"valid"],[[7804,7804],"mapped",[7805]],[[7805,7805],"valid"],[[7806,7806],"mapped",[7807]],[[7807,7807],"valid"],[[7808,7808],"mapped",[7809]],[[7809,7809],"valid"],[[7810,7810],"mapped",[7811]],[[7811,7811],"valid"],[[7812,7812],"mapped",[7813]],[[7813,7813],"valid"],[[7814,7814],"mapped",[7815]],[[7815,7815],"valid"],[[7816,7816],"mapped",[7817]],[[7817,7817],"valid"],[[7818,7818],"mapped",[7819]],[[7819,7819],"valid"],[[7820,7820],"mapped",[7821]],[[7821,7821],"valid"],[[7822,7822],"mapped",[7823]],[[7823,7823],"valid"],[[7824,7824],"mapped",[7825]],[[7825,7825],"valid"],[[7826,7826],"mapped",[7827]],[[7827,7827],"valid"],[[7828,7828],"mapped",[7829]],[[7829,7833],"valid"],[[7834,7834],"mapped",[97,702]],[[7835,7835],"mapped",[7777]],[[7836,7837],"valid"],[[7838,7838],"mapped",[115,115]],[[7839,7839],"valid"],[[7840,7840],"mapped",[7841]],[[7841,7841],"valid"],[[7842,7842],"mapped",[7843]],[[7843,7843],"valid"],[[7844,7844],"mapped",[7845]],[[7845,7845],"valid"],[[7846,7846],"mapped",[7847]],[[7847,7847],"valid"],[[7848,7848],"mapped",[7849]],[[7849,7849],"valid"],[[7850,7850],"mapped",[7851]],[[7851,7851],"valid"],[[7852,7852],"mapped",[7853]],[[7853,7853],"valid"],[[7854,7854],"mapped",[7855]],[[7855,7855],"valid"],[[7856,7856],"mapped",[7857]],[[7857,7857],"valid"],[[7858,7858],"mapped",[7859]],[[7859,7859],"valid"],[[7860,7860],"mapped",[7861]],[[7861,7861],"valid"],[[7862,7862],"mapped",[7863]],[[7863,7863],"valid"],[[7864,7864],"mapped",[7865]],[[7865,7865],"valid"],[[7866,7866],"mapped",[7867]],[[7867,7867],"valid"],[[7868,7868],"mapped",[7869]],[[7869,7869],"valid"],[[7870,7870],"mapped",[7871]],[[7871,7871],"valid"],[[7872,7872],"mapped",[7873]],[[7873,7873],"valid"],[[7874,7874],"mapped",[7875]],[[7875,7875],"valid"],[[7876,7876],"mapped",[7877]],[[7877,7877],"valid"],[[7878,7878],"mapped",[7879]],[[7879,7879],"valid"],[[7880,7880],"mapped",[7881]],[[7881,7881],"valid"],[[7882,7882],"mapped",[7883]],[[7883,7883],"valid"],[[7884,7884],"mapped",[7885]],[[7885,7885],"valid"],[[7886,7886],"mapped",[7887]],[[7887,7887],"valid"],[[7888,7888],"mapped",[7889]],[[7889,7889],"valid"],[[7890,7890],"mapped",[7891]],[[7891,7891],"valid"],[[7892,7892],"mapped",[7893]],[[7893,7893],"valid"],[[7894,7894],"mapped",[7895]],[[7895,7895],"valid"],[[7896,7896],"mapped",[7897]],[[7897,7897],"valid"],[[7898,7898],"mapped",[7899]],[[7899,7899],"valid"],[[7900,7900],"mapped",[7901]],[[7901,7901],"valid"],[[7902,7902],"mapped",[7903]],[[7903,7903],"valid"],[[7904,7904],"mapped",[7905]],[[7905,7905],"valid"],[[7906,7906],"mapped",[7907]],[[7907,7907],"valid"],[[7908,7908],"mapped",[7909]],[[7909,7909],"valid"],[[7910,7910],"mapped",[7911]],[[7911,7911],"valid"],[[7912,7912],"mapped",[7913]],[[7913,7913],"valid"],[[7914,7914],"mapped",[7915]],[[7915,7915],"valid"],[[7916,7916],"mapped",[7917]],[[7917,7917],"valid"],[[7918,7918],"mapped",[7919]],[[7919,7919],"valid"],[[7920,7920],"mapped",[7921]],[[7921,7921],"valid"],[[7922,7922],"mapped",[7923]],[[7923,7923],"valid"],[[7924,7924],"mapped",[7925]],[[7925,7925],"valid"],[[7926,7926],"mapped",[7927]],[[7927,7927],"valid"],[[7928,7928],"mapped",[7929]],[[7929,7929],"valid"],[[7930,7930],"mapped",[7931]],[[7931,7931],"valid"],[[7932,7932],"mapped",[7933]],[[7933,7933],"valid"],[[7934,7934],"mapped",[7935]],[[7935,7935],"valid"],[[7936,7943],"valid"],[[7944,7944],"mapped",[7936]],[[7945,7945],"mapped",[7937]],[[7946,7946],"mapped",[7938]],[[7947,7947],"mapped",[7939]],[[7948,7948],"mapped",[7940]],[[7949,7949],"mapped",[7941]],[[7950,7950],"mapped",[7942]],[[7951,7951],"mapped",[7943]],[[7952,7957],"valid"],[[7958,7959],"disallowed"],[[7960,7960],"mapped",[7952]],[[7961,7961],"mapped",[7953]],[[7962,7962],"mapped",[7954]],[[7963,7963],"mapped",[7955]],[[7964,7964],"mapped",[7956]],[[7965,7965],"mapped",[7957]],[[7966,7967],"disallowed"],[[7968,7975],"valid"],[[7976,7976],"mapped",[7968]],[[7977,7977],"mapped",[7969]],[[7978,7978],"mapped",[7970]],[[7979,7979],"mapped",[7971]],[[7980,7980],"mapped",[7972]],[[7981,7981],"mapped",[7973]],[[7982,7982],"mapped",[7974]],[[7983,7983],"mapped",[7975]],[[7984,7991],"valid"],[[7992,7992],"mapped",[7984]],[[7993,7993],"mapped",[7985]],[[7994,7994],"mapped",[7986]],[[7995,7995],"mapped",[7987]],[[7996,7996],"mapped",[7988]],[[7997,7997],"mapped",[7989]],[[7998,7998],"mapped",[7990]],[[7999,7999],"mapped",[7991]],[[8000,8005],"valid"],[[8006,8007],"disallowed"],[[8008,8008],"mapped",[8000]],[[8009,8009],"mapped",[8001]],[[8010,8010],"mapped",[8002]],[[8011,8011],"mapped",[8003]],[[8012,8012],"mapped",[8004]],[[8013,8013],"mapped",[8005]],[[8014,8015],"disallowed"],[[8016,8023],"valid"],[[8024,8024],"disallowed"],[[8025,8025],"mapped",[8017]],[[8026,8026],"disallowed"],[[8027,8027],"mapped",[8019]],[[8028,8028],"disallowed"],[[8029,8029],"mapped",[8021]],[[8030,8030],"disallowed"],[[8031,8031],"mapped",[8023]],[[8032,8039],"valid"],[[8040,8040],"mapped",[8032]],[[8041,8041],"mapped",[8033]],[[8042,8042],"mapped",[8034]],[[8043,8043],"mapped",[8035]],[[8044,8044],"mapped",[8036]],[[8045,8045],"mapped",[8037]],[[8046,8046],"mapped",[8038]],[[8047,8047],"mapped",[8039]],[[8048,8048],"valid"],[[8049,8049],"mapped",[940]],[[8050,8050],"valid"],[[8051,8051],"mapped",[941]],[[8052,8052],"valid"],[[8053,8053],"mapped",[942]],[[8054,8054],"valid"],[[8055,8055],"mapped",[943]],[[8056,8056],"valid"],[[8057,8057],"mapped",[972]],[[8058,8058],"valid"],[[8059,8059],"mapped",[973]],[[8060,8060],"valid"],[[8061,8061],"mapped",[974]],[[8062,8063],"disallowed"],[[8064,8064],"mapped",[7936,953]],[[8065,8065],"mapped",[7937,953]],[[8066,8066],"mapped",[7938,953]],[[8067,8067],"mapped",[7939,953]],[[8068,8068],"mapped",[7940,953]],[[8069,8069],"mapped",[7941,953]],[[8070,8070],"mapped",[7942,953]],[[8071,8071],"mapped",[7943,953]],[[8072,8072],"mapped",[7936,953]],[[8073,8073],"mapped",[7937,953]],[[8074,8074],"mapped",[7938,953]],[[8075,8075],"mapped",[7939,953]],[[8076,8076],"mapped",[7940,953]],[[8077,8077],"mapped",[7941,953]],[[8078,8078],"mapped",[7942,953]],[[8079,8079],"mapped",[7943,953]],[[8080,8080],"mapped",[7968,953]],[[8081,8081],"mapped",[7969,953]],[[8082,8082],"mapped",[7970,953]],[[8083,8083],"mapped",[7971,953]],[[8084,8084],"mapped",[7972,953]],[[8085,8085],"mapped",[7973,953]],[[8086,8086],"mapped",[7974,953]],[[8087,8087],"mapped",[7975,953]],[[8088,8088],"mapped",[7968,953]],[[8089,8089],"mapped",[7969,953]],[[8090,8090],"mapped",[7970,953]],[[8091,8091],"mapped",[7971,953]],[[8092,8092],"mapped",[7972,953]],[[8093,8093],"mapped",[7973,953]],[[8094,8094],"mapped",[7974,953]],[[8095,8095],"mapped",[7975,953]],[[8096,8096],"mapped",[8032,953]],[[8097,8097],"mapped",[8033,953]],[[8098,8098],"mapped",[8034,953]],[[8099,8099],"mapped",[8035,953]],[[8100,8100],"mapped",[8036,953]],[[8101,8101],"mapped",[8037,953]],[[8102,8102],"mapped",[8038,953]],[[8103,8103],"mapped",[8039,953]],[[8104,8104],"mapped",[8032,953]],[[8105,8105],"mapped",[8033,953]],[[8106,8106],"mapped",[8034,953]],[[8107,8107],"mapped",[8035,953]],[[8108,8108],"mapped",[8036,953]],[[8109,8109],"mapped",[8037,953]],[[8110,8110],"mapped",[8038,953]],[[8111,8111],"mapped",[8039,953]],[[8112,8113],"valid"],[[8114,8114],"mapped",[8048,953]],[[8115,8115],"mapped",[945,953]],[[8116,8116],"mapped",[940,953]],[[8117,8117],"disallowed"],[[8118,8118],"valid"],[[8119,8119],"mapped",[8118,953]],[[8120,8120],"mapped",[8112]],[[8121,8121],"mapped",[8113]],[[8122,8122],"mapped",[8048]],[[8123,8123],"mapped",[940]],[[8124,8124],"mapped",[945,953]],[[8125,8125],"disallowed_STD3_mapped",[32,787]],[[8126,8126],"mapped",[953]],[[8127,8127],"disallowed_STD3_mapped",[32,787]],[[8128,8128],"disallowed_STD3_mapped",[32,834]],[[8129,8129],"disallowed_STD3_mapped",[32,776,834]],[[8130,8130],"mapped",[8052,953]],[[8131,8131],"mapped",[951,953]],[[8132,8132],"mapped",[942,953]],[[8133,8133],"disallowed"],[[8134,8134],"valid"],[[8135,8135],"mapped",[8134,953]],[[8136,8136],"mapped",[8050]],[[8137,8137],"mapped",[941]],[[8138,8138],"mapped",[8052]],[[8139,8139],"mapped",[942]],[[8140,8140],"mapped",[951,953]],[[8141,8141],"disallowed_STD3_mapped",[32,787,768]],[[8142,8142],"disallowed_STD3_mapped",[32,787,769]],[[8143,8143],"disallowed_STD3_mapped",[32,787,834]],[[8144,8146],"valid"],[[8147,8147],"mapped",[912]],[[8148,8149],"disallowed"],[[8150,8151],"valid"],[[8152,8152],"mapped",[8144]],[[8153,8153],"mapped",[8145]],[[8154,8154],"mapped",[8054]],[[8155,8155],"mapped",[943]],[[8156,8156],"disallowed"],[[8157,8157],"disallowed_STD3_mapped",[32,788,768]],[[8158,8158],"disallowed_STD3_mapped",[32,788,769]],[[8159,8159],"disallowed_STD3_mapped",[32,788,834]],[[8160,8162],"valid"],[[8163,8163],"mapped",[944]],[[8164,8167],"valid"],[[8168,8168],"mapped",[8160]],[[8169,8169],"mapped",[8161]],[[8170,8170],"mapped",[8058]],[[8171,8171],"mapped",[973]],[[8172,8172],"mapped",[8165]],[[8173,8173],"disallowed_STD3_mapped",[32,776,768]],[[8174,8174],"disallowed_STD3_mapped",[32,776,769]],[[8175,8175],"disallowed_STD3_mapped",[96]],[[8176,8177],"disallowed"],[[8178,8178],"mapped",[8060,953]],[[8179,8179],"mapped",[969,953]],[[8180,8180],"mapped",[974,953]],[[8181,8181],"disallowed"],[[8182,8182],"valid"],[[8183,8183],"mapped",[8182,953]],[[8184,8184],"mapped",[8056]],[[8185,8185],"mapped",[972]],[[8186,8186],"mapped",[8060]],[[8187,8187],"mapped",[974]],[[8188,8188],"mapped",[969,953]],[[8189,8189],"disallowed_STD3_mapped",[32,769]],[[8190,8190],"disallowed_STD3_mapped",[32,788]],[[8191,8191],"disallowed"],[[8192,8202],"disallowed_STD3_mapped",[32]],[[8203,8203],"ignored"],[[8204,8205],"deviation",[]],[[8206,8207],"disallowed"],[[8208,8208],"valid",[],"NV8"],[[8209,8209],"mapped",[8208]],[[8210,8214],"valid",[],"NV8"],[[8215,8215],"disallowed_STD3_mapped",[32,819]],[[8216,8227],"valid",[],"NV8"],[[8228,8230],"disallowed"],[[8231,8231],"valid",[],"NV8"],[[8232,8238],"disallowed"],[[8239,8239],"disallowed_STD3_mapped",[32]],[[8240,8242],"valid",[],"NV8"],[[8243,8243],"mapped",[8242,8242]],[[8244,8244],"mapped",[8242,8242,8242]],[[8245,8245],"valid",[],"NV8"],[[8246,8246],"mapped",[8245,8245]],[[8247,8247],"mapped",[8245,8245,8245]],[[8248,8251],"valid",[],"NV8"],[[8252,8252],"disallowed_STD3_mapped",[33,33]],[[8253,8253],"valid",[],"NV8"],[[8254,8254],"disallowed_STD3_mapped",[32,773]],[[8255,8262],"valid",[],"NV8"],[[8263,8263],"disallowed_STD3_mapped",[63,63]],[[8264,8264],"disallowed_STD3_mapped",[63,33]],[[8265,8265],"disallowed_STD3_mapped",[33,63]],[[8266,8269],"valid",[],"NV8"],[[8270,8274],"valid",[],"NV8"],[[8275,8276],"valid",[],"NV8"],[[8277,8278],"valid",[],"NV8"],[[8279,8279],"mapped",[8242,8242,8242,8242]],[[8280,8286],"valid",[],"NV8"],[[8287,8287],"disallowed_STD3_mapped",[32]],[[8288,8288],"ignored"],[[8289,8291],"disallowed"],[[8292,8292],"ignored"],[[8293,8293],"disallowed"],[[8294,8297],"disallowed"],[[8298,8303],"disallowed"],[[8304,8304],"mapped",[48]],[[8305,8305],"mapped",[105]],[[8306,8307],"disallowed"],[[8308,8308],"mapped",[52]],[[8309,8309],"mapped",[53]],[[8310,8310],"mapped",[54]],[[8311,8311],"mapped",[55]],[[8312,8312],"mapped",[56]],[[8313,8313],"mapped",[57]],[[8314,8314],"disallowed_STD3_mapped",[43]],[[8315,8315],"mapped",[8722]],[[8316,8316],"disallowed_STD3_mapped",[61]],[[8317,8317],"disallowed_STD3_mapped",[40]],[[8318,8318],"disallowed_STD3_mapped",[41]],[[8319,8319],"mapped",[110]],[[8320,8320],"mapped",[48]],[[8321,8321],"mapped",[49]],[[8322,8322],"mapped",[50]],[[8323,8323],"mapped",[51]],[[8324,8324],"mapped",[52]],[[8325,8325],"mapped",[53]],[[8326,8326],"mapped",[54]],[[8327,8327],"mapped",[55]],[[8328,8328],"mapped",[56]],[[8329,8329],"mapped",[57]],[[8330,8330],"disallowed_STD3_mapped",[43]],[[8331,8331],"mapped",[8722]],[[8332,8332],"disallowed_STD3_mapped",[61]],[[8333,8333],"disallowed_STD3_mapped",[40]],[[8334,8334],"disallowed_STD3_mapped",[41]],[[8335,8335],"disallowed"],[[8336,8336],"mapped",[97]],[[8337,8337],"mapped",[101]],[[8338,8338],"mapped",[111]],[[8339,8339],"mapped",[120]],[[8340,8340],"mapped",[601]],[[8341,8341],"mapped",[104]],[[8342,8342],"mapped",[107]],[[8343,8343],"mapped",[108]],[[8344,8344],"mapped",[109]],[[8345,8345],"mapped",[110]],[[8346,8346],"mapped",[112]],[[8347,8347],"mapped",[115]],[[8348,8348],"mapped",[116]],[[8349,8351],"disallowed"],[[8352,8359],"valid",[],"NV8"],[[8360,8360],"mapped",[114,115]],[[8361,8362],"valid",[],"NV8"],[[8363,8363],"valid",[],"NV8"],[[8364,8364],"valid",[],"NV8"],[[8365,8367],"valid",[],"NV8"],[[8368,8369],"valid",[],"NV8"],[[8370,8373],"valid",[],"NV8"],[[8374,8376],"valid",[],"NV8"],[[8377,8377],"valid",[],"NV8"],[[8378,8378],"valid",[],"NV8"],[[8379,8381],"valid",[],"NV8"],[[8382,8382],"valid",[],"NV8"],[[8383,8399],"disallowed"],[[8400,8417],"valid",[],"NV8"],[[8418,8419],"valid",[],"NV8"],[[8420,8426],"valid",[],"NV8"],[[8427,8427],"valid",[],"NV8"],[[8428,8431],"valid",[],"NV8"],[[8432,8432],"valid",[],"NV8"],[[8433,8447],"disallowed"],[[8448,8448],"disallowed_STD3_mapped",[97,47,99]],[[8449,8449],"disallowed_STD3_mapped",[97,47,115]],[[8450,8450],"mapped",[99]],[[8451,8451],"mapped",[176,99]],[[8452,8452],"valid",[],"NV8"],[[8453,8453],"disallowed_STD3_mapped",[99,47,111]],[[8454,8454],"disallowed_STD3_mapped",[99,47,117]],[[8455,8455],"mapped",[603]],[[8456,8456],"valid",[],"NV8"],[[8457,8457],"mapped",[176,102]],[[8458,8458],"mapped",[103]],[[8459,8462],"mapped",[104]],[[8463,8463],"mapped",[295]],[[8464,8465],"mapped",[105]],[[8466,8467],"mapped",[108]],[[8468,8468],"valid",[],"NV8"],[[8469,8469],"mapped",[110]],[[8470,8470],"mapped",[110,111]],[[8471,8472],"valid",[],"NV8"],[[8473,8473],"mapped",[112]],[[8474,8474],"mapped",[113]],[[8475,8477],"mapped",[114]],[[8478,8479],"valid",[],"NV8"],[[8480,8480],"mapped",[115,109]],[[8481,8481],"mapped",[116,101,108]],[[8482,8482],"mapped",[116,109]],[[8483,8483],"valid",[],"NV8"],[[8484,8484],"mapped",[122]],[[8485,8485],"valid",[],"NV8"],[[8486,8486],"mapped",[969]],[[8487,8487],"valid",[],"NV8"],[[8488,8488],"mapped",[122]],[[8489,8489],"valid",[],"NV8"],[[8490,8490],"mapped",[107]],[[8491,8491],"mapped",[229]],[[8492,8492],"mapped",[98]],[[8493,8493],"mapped",[99]],[[8494,8494],"valid",[],"NV8"],[[8495,8496],"mapped",[101]],[[8497,8497],"mapped",[102]],[[8498,8498],"disallowed"],[[8499,8499],"mapped",[109]],[[8500,8500],"mapped",[111]],[[8501,8501],"mapped",[1488]],[[8502,8502],"mapped",[1489]],[[8503,8503],"mapped",[1490]],[[8504,8504],"mapped",[1491]],[[8505,8505],"mapped",[105]],[[8506,8506],"valid",[],"NV8"],[[8507,8507],"mapped",[102,97,120]],[[8508,8508],"mapped",[960]],[[8509,8510],"mapped",[947]],[[8511,8511],"mapped",[960]],[[8512,8512],"mapped",[8721]],[[8513,8516],"valid",[],"NV8"],[[8517,8518],"mapped",[100]],[[8519,8519],"mapped",[101]],[[8520,8520],"mapped",[105]],[[8521,8521],"mapped",[106]],[[8522,8523],"valid",[],"NV8"],[[8524,8524],"valid",[],"NV8"],[[8525,8525],"valid",[],"NV8"],[[8526,8526],"valid"],[[8527,8527],"valid",[],"NV8"],[[8528,8528],"mapped",[49,8260,55]],[[8529,8529],"mapped",[49,8260,57]],[[8530,8530],"mapped",[49,8260,49,48]],[[8531,8531],"mapped",[49,8260,51]],[[8532,8532],"mapped",[50,8260,51]],[[8533,8533],"mapped",[49,8260,53]],[[8534,8534],"mapped",[50,8260,53]],[[8535,8535],"mapped",[51,8260,53]],[[8536,8536],"mapped",[52,8260,53]],[[8537,8537],"mapped",[49,8260,54]],[[8538,8538],"mapped",[53,8260,54]],[[8539,8539],"mapped",[49,8260,56]],[[8540,8540],"mapped",[51,8260,56]],[[8541,8541],"mapped",[53,8260,56]],[[8542,8542],"mapped",[55,8260,56]],[[8543,8543],"mapped",[49,8260]],[[8544,8544],"mapped",[105]],[[8545,8545],"mapped",[105,105]],[[8546,8546],"mapped",[105,105,105]],[[8547,8547],"mapped",[105,118]],[[8548,8548],"mapped",[118]],[[8549,8549],"mapped",[118,105]],[[8550,8550],"mapped",[118,105,105]],[[8551,8551],"mapped",[118,105,105,105]],[[8552,8552],"mapped",[105,120]],[[8553,8553],"mapped",[120]],[[8554,8554],"mapped",[120,105]],[[8555,8555],"mapped",[120,105,105]],[[8556,8556],"mapped",[108]],[[8557,8557],"mapped",[99]],[[8558,8558],"mapped",[100]],[[8559,8559],"mapped",[109]],[[8560,8560],"mapped",[105]],[[8561,8561],"mapped",[105,105]],[[8562,8562],"mapped",[105,105,105]],[[8563,8563],"mapped",[105,118]],[[8564,8564],"mapped",[118]],[[8565,8565],"mapped",[118,105]],[[8566,8566],"mapped",[118,105,105]],[[8567,8567],"mapped",[118,105,105,105]],[[8568,8568],"mapped",[105,120]],[[8569,8569],"mapped",[120]],[[8570,8570],"mapped",[120,105]],[[8571,8571],"mapped",[120,105,105]],[[8572,8572],"mapped",[108]],[[8573,8573],"mapped",[99]],[[8574,8574],"mapped",[100]],[[8575,8575],"mapped",[109]],[[8576,8578],"valid",[],"NV8"],[[8579,8579],"disallowed"],[[8580,8580],"valid"],[[8581,8584],"valid",[],"NV8"],[[8585,8585],"mapped",[48,8260,51]],[[8586,8587],"valid",[],"NV8"],[[8588,8591],"disallowed"],[[8592,8682],"valid",[],"NV8"],[[8683,8691],"valid",[],"NV8"],[[8692,8703],"valid",[],"NV8"],[[8704,8747],"valid",[],"NV8"],[[8748,8748],"mapped",[8747,8747]],[[8749,8749],"mapped",[8747,8747,8747]],[[8750,8750],"valid",[],"NV8"],[[8751,8751],"mapped",[8750,8750]],[[8752,8752],"mapped",[8750,8750,8750]],[[8753,8799],"valid",[],"NV8"],[[8800,8800],"disallowed_STD3_valid"],[[8801,8813],"valid",[],"NV8"],[[8814,8815],"disallowed_STD3_valid"],[[8816,8945],"valid",[],"NV8"],[[8946,8959],"valid",[],"NV8"],[[8960,8960],"valid",[],"NV8"],[[8961,8961],"valid",[],"NV8"],[[8962,9000],"valid",[],"NV8"],[[9001,9001],"mapped",[12296]],[[9002,9002],"mapped",[12297]],[[9003,9082],"valid",[],"NV8"],[[9083,9083],"valid",[],"NV8"],[[9084,9084],"valid",[],"NV8"],[[9085,9114],"valid",[],"NV8"],[[9115,9166],"valid",[],"NV8"],[[9167,9168],"valid",[],"NV8"],[[9169,9179],"valid",[],"NV8"],[[9180,9191],"valid",[],"NV8"],[[9192,9192],"valid",[],"NV8"],[[9193,9203],"valid",[],"NV8"],[[9204,9210],"valid",[],"NV8"],[[9211,9215],"disallowed"],[[9216,9252],"valid",[],"NV8"],[[9253,9254],"valid",[],"NV8"],[[9255,9279],"disallowed"],[[9280,9290],"valid",[],"NV8"],[[9291,9311],"disallowed"],[[9312,9312],"mapped",[49]],[[9313,9313],"mapped",[50]],[[9314,9314],"mapped",[51]],[[9315,9315],"mapped",[52]],[[9316,9316],"mapped",[53]],[[9317,9317],"mapped",[54]],[[9318,9318],"mapped",[55]],[[9319,9319],"mapped",[56]],[[9320,9320],"mapped",[57]],[[9321,9321],"mapped",[49,48]],[[9322,9322],"mapped",[49,49]],[[9323,9323],"mapped",[49,50]],[[9324,9324],"mapped",[49,51]],[[9325,9325],"mapped",[49,52]],[[9326,9326],"mapped",[49,53]],[[9327,9327],"mapped",[49,54]],[[9328,9328],"mapped",[49,55]],[[9329,9329],"mapped",[49,56]],[[9330,9330],"mapped",[49,57]],[[9331,9331],"mapped",[50,48]],[[9332,9332],"disallowed_STD3_mapped",[40,49,41]],[[9333,9333],"disallowed_STD3_mapped",[40,50,41]],[[9334,9334],"disallowed_STD3_mapped",[40,51,41]],[[9335,9335],"disallowed_STD3_mapped",[40,52,41]],[[9336,9336],"disallowed_STD3_mapped",[40,53,41]],[[9337,9337],"disallowed_STD3_mapped",[40,54,41]],[[9338,9338],"disallowed_STD3_mapped",[40,55,41]],[[9339,9339],"disallowed_STD3_mapped",[40,56,41]],[[9340,9340],"disallowed_STD3_mapped",[40,57,41]],[[9341,9341],"disallowed_STD3_mapped",[40,49,48,41]],[[9342,9342],"disallowed_STD3_mapped",[40,49,49,41]],[[9343,9343],"disallowed_STD3_mapped",[40,49,50,41]],[[9344,9344],"disallowed_STD3_mapped",[40,49,51,41]],[[9345,9345],"disallowed_STD3_mapped",[40,49,52,41]],[[9346,9346],"disallowed_STD3_mapped",[40,49,53,41]],[[9347,9347],"disallowed_STD3_mapped",[40,49,54,41]],[[9348,9348],"disallowed_STD3_mapped",[40,49,55,41]],[[9349,9349],"disallowed_STD3_mapped",[40,49,56,41]],[[9350,9350],"disallowed_STD3_mapped",[40,49,57,41]],[[9351,9351],"disallowed_STD3_mapped",[40,50,48,41]],[[9352,9371],"disallowed"],[[9372,9372],"disallowed_STD3_mapped",[40,97,41]],[[9373,9373],"disallowed_STD3_mapped",[40,98,41]],[[9374,9374],"disallowed_STD3_mapped",[40,99,41]],[[9375,9375],"disallowed_STD3_mapped",[40,100,41]],[[9376,9376],"disallowed_STD3_mapped",[40,101,41]],[[9377,9377],"disallowed_STD3_mapped",[40,102,41]],[[9378,9378],"disallowed_STD3_mapped",[40,103,41]],[[9379,9379],"disallowed_STD3_mapped",[40,104,41]],[[9380,9380],"disallowed_STD3_mapped",[40,105,41]],[[9381,9381],"disallowed_STD3_mapped",[40,106,41]],[[9382,9382],"disallowed_STD3_mapped",[40,107,41]],[[9383,9383],"disallowed_STD3_mapped",[40,108,41]],[[9384,9384],"disallowed_STD3_mapped",[40,109,41]],[[9385,9385],"disallowed_STD3_mapped",[40,110,41]],[[9386,9386],"disallowed_STD3_mapped",[40,111,41]],[[9387,9387],"disallowed_STD3_mapped",[40,112,41]],[[9388,9388],"disallowed_STD3_mapped",[40,113,41]],[[9389,9389],"disallowed_STD3_mapped",[40,114,41]],[[9390,9390],"disallowed_STD3_mapped",[40,115,41]],[[9391,9391],"disallowed_STD3_mapped",[40,116,41]],[[9392,9392],"disallowed_STD3_mapped",[40,117,41]],[[9393,9393],"disallowed_STD3_mapped",[40,118,41]],[[9394,9394],"disallowed_STD3_mapped",[40,119,41]],[[9395,9395],"disallowed_STD3_mapped",[40,120,41]],[[9396,9396],"disallowed_STD3_mapped",[40,121,41]],[[9397,9397],"disallowed_STD3_mapped",[40,122,41]],[[9398,9398],"mapped",[97]],[[9399,9399],"mapped",[98]],[[9400,9400],"mapped",[99]],[[9401,9401],"mapped",[100]],[[9402,9402],"mapped",[101]],[[9403,9403],"mapped",[102]],[[9404,9404],"mapped",[103]],[[9405,9405],"mapped",[104]],[[9406,9406],"mapped",[105]],[[9407,9407],"mapped",[106]],[[9408,9408],"mapped",[107]],[[9409,9409],"mapped",[108]],[[9410,9410],"mapped",[109]],[[9411,9411],"mapped",[110]],[[9412,9412],"mapped",[111]],[[9413,9413],"mapped",[112]],[[9414,9414],"mapped",[113]],[[9415,9415],"mapped",[114]],[[9416,9416],"mapped",[115]],[[9417,9417],"mapped",[116]],[[9418,9418],"mapped",[117]],[[9419,9419],"mapped",[118]],[[9420,9420],"mapped",[119]],[[9421,9421],"mapped",[120]],[[9422,9422],"mapped",[121]],[[9423,9423],"mapped",[122]],[[9424,9424],"mapped",[97]],[[9425,9425],"mapped",[98]],[[9426,9426],"mapped",[99]],[[9427,9427],"mapped",[100]],[[9428,9428],"mapped",[101]],[[9429,9429],"mapped",[102]],[[9430,9430],"mapped",[103]],[[9431,9431],"mapped",[104]],[[9432,9432],"mapped",[105]],[[9433,9433],"mapped",[106]],[[9434,9434],"mapped",[107]],[[9435,9435],"mapped",[108]],[[9436,9436],"mapped",[109]],[[9437,9437],"mapped",[110]],[[9438,9438],"mapped",[111]],[[9439,9439],"mapped",[112]],[[9440,9440],"mapped",[113]],[[9441,9441],"mapped",[114]],[[9442,9442],"mapped",[115]],[[9443,9443],"mapped",[116]],[[9444,9444],"mapped",[117]],[[9445,9445],"mapped",[118]],[[9446,9446],"mapped",[119]],[[9447,9447],"mapped",[120]],[[9448,9448],"mapped",[121]],[[9449,9449],"mapped",[122]],[[9450,9450],"mapped",[48]],[[9451,9470],"valid",[],"NV8"],[[9471,9471],"valid",[],"NV8"],[[9472,9621],"valid",[],"NV8"],[[9622,9631],"valid",[],"NV8"],[[9632,9711],"valid",[],"NV8"],[[9712,9719],"valid",[],"NV8"],[[9720,9727],"valid",[],"NV8"],[[9728,9747],"valid",[],"NV8"],[[9748,9749],"valid",[],"NV8"],[[9750,9751],"valid",[],"NV8"],[[9752,9752],"valid",[],"NV8"],[[9753,9753],"valid",[],"NV8"],[[9754,9839],"valid",[],"NV8"],[[9840,9841],"valid",[],"NV8"],[[9842,9853],"valid",[],"NV8"],[[9854,9855],"valid",[],"NV8"],[[9856,9865],"valid",[],"NV8"],[[9866,9873],"valid",[],"NV8"],[[9874,9884],"valid",[],"NV8"],[[9885,9885],"valid",[],"NV8"],[[9886,9887],"valid",[],"NV8"],[[9888,9889],"valid",[],"NV8"],[[9890,9905],"valid",[],"NV8"],[[9906,9906],"valid",[],"NV8"],[[9907,9916],"valid",[],"NV8"],[[9917,9919],"valid",[],"NV8"],[[9920,9923],"valid",[],"NV8"],[[9924,9933],"valid",[],"NV8"],[[9934,9934],"valid",[],"NV8"],[[9935,9953],"valid",[],"NV8"],[[9954,9954],"valid",[],"NV8"],[[9955,9955],"valid",[],"NV8"],[[9956,9959],"valid",[],"NV8"],[[9960,9983],"valid",[],"NV8"],[[9984,9984],"valid",[],"NV8"],[[9985,9988],"valid",[],"NV8"],[[9989,9989],"valid",[],"NV8"],[[9990,9993],"valid",[],"NV8"],[[9994,9995],"valid",[],"NV8"],[[9996,10023],"valid",[],"NV8"],[[10024,10024],"valid",[],"NV8"],[[10025,10059],"valid",[],"NV8"],[[10060,10060],"valid",[],"NV8"],[[10061,10061],"valid",[],"NV8"],[[10062,10062],"valid",[],"NV8"],[[10063,10066],"valid",[],"NV8"],[[10067,10069],"valid",[],"NV8"],[[10070,10070],"valid",[],"NV8"],[[10071,10071],"valid",[],"NV8"],[[10072,10078],"valid",[],"NV8"],[[10079,10080],"valid",[],"NV8"],[[10081,10087],"valid",[],"NV8"],[[10088,10101],"valid",[],"NV8"],[[10102,10132],"valid",[],"NV8"],[[10133,10135],"valid",[],"NV8"],[[10136,10159],"valid",[],"NV8"],[[10160,10160],"valid",[],"NV8"],[[10161,10174],"valid",[],"NV8"],[[10175,10175],"valid",[],"NV8"],[[10176,10182],"valid",[],"NV8"],[[10183,10186],"valid",[],"NV8"],[[10187,10187],"valid",[],"NV8"],[[10188,10188],"valid",[],"NV8"],[[10189,10189],"valid",[],"NV8"],[[10190,10191],"valid",[],"NV8"],[[10192,10219],"valid",[],"NV8"],[[10220,10223],"valid",[],"NV8"],[[10224,10239],"valid",[],"NV8"],[[10240,10495],"valid",[],"NV8"],[[10496,10763],"valid",[],"NV8"],[[10764,10764],"mapped",[8747,8747,8747,8747]],[[10765,10867],"valid",[],"NV8"],[[10868,10868],"disallowed_STD3_mapped",[58,58,61]],[[10869,10869],"disallowed_STD3_mapped",[61,61]],[[10870,10870],"disallowed_STD3_mapped",[61,61,61]],[[10871,10971],"valid",[],"NV8"],[[10972,10972],"mapped",[10973,824]],[[10973,11007],"valid",[],"NV8"],[[11008,11021],"valid",[],"NV8"],[[11022,11027],"valid",[],"NV8"],[[11028,11034],"valid",[],"NV8"],[[11035,11039],"valid",[],"NV8"],[[11040,11043],"valid",[],"NV8"],[[11044,11084],"valid",[],"NV8"],[[11085,11087],"valid",[],"NV8"],[[11088,11092],"valid",[],"NV8"],[[11093,11097],"valid",[],"NV8"],[[11098,11123],"valid",[],"NV8"],[[11124,11125],"disallowed"],[[11126,11157],"valid",[],"NV8"],[[11158,11159],"disallowed"],[[11160,11193],"valid",[],"NV8"],[[11194,11196],"disallowed"],[[11197,11208],"valid",[],"NV8"],[[11209,11209],"disallowed"],[[11210,11217],"valid",[],"NV8"],[[11218,11243],"disallowed"],[[11244,11247],"valid",[],"NV8"],[[11248,11263],"disallowed"],[[11264,11264],"mapped",[11312]],[[11265,11265],"mapped",[11313]],[[11266,11266],"mapped",[11314]],[[11267,11267],"mapped",[11315]],[[11268,11268],"mapped",[11316]],[[11269,11269],"mapped",[11317]],[[11270,11270],"mapped",[11318]],[[11271,11271],"mapped",[11319]],[[11272,11272],"mapped",[11320]],[[11273,11273],"mapped",[11321]],[[11274,11274],"mapped",[11322]],[[11275,11275],"mapped",[11323]],[[11276,11276],"mapped",[11324]],[[11277,11277],"mapped",[11325]],[[11278,11278],"mapped",[11326]],[[11279,11279],"mapped",[11327]],[[11280,11280],"mapped",[11328]],[[11281,11281],"mapped",[11329]],[[11282,11282],"mapped",[11330]],[[11283,11283],"mapped",[11331]],[[11284,11284],"mapped",[11332]],[[11285,11285],"mapped",[11333]],[[11286,11286],"mapped",[11334]],[[11287,11287],"mapped",[11335]],[[11288,11288],"mapped",[11336]],[[11289,11289],"mapped",[11337]],[[11290,11290],"mapped",[11338]],[[11291,11291],"mapped",[11339]],[[11292,11292],"mapped",[11340]],[[11293,11293],"mapped",[11341]],[[11294,11294],"mapped",[11342]],[[11295,11295],"mapped",[11343]],[[11296,11296],"mapped",[11344]],[[11297,11297],"mapped",[11345]],[[11298,11298],"mapped",[11346]],[[11299,11299],"mapped",[11347]],[[11300,11300],"mapped",[11348]],[[11301,11301],"mapped",[11349]],[[11302,11302],"mapped",[11350]],[[11303,11303],"mapped",[11351]],[[11304,11304],"mapped",[11352]],[[11305,11305],"mapped",[11353]],[[11306,11306],"mapped",[11354]],[[11307,11307],"mapped",[11355]],[[11308,11308],"mapped",[11356]],[[11309,11309],"mapped",[11357]],[[11310,11310],"mapped",[11358]],[[11311,11311],"disallowed"],[[11312,11358],"valid"],[[11359,11359],"disallowed"],[[11360,11360],"mapped",[11361]],[[11361,11361],"valid"],[[11362,11362],"mapped",[619]],[[11363,11363],"mapped",[7549]],[[11364,11364],"mapped",[637]],[[11365,11366],"valid"],[[11367,11367],"mapped",[11368]],[[11368,11368],"valid"],[[11369,11369],"mapped",[11370]],[[11370,11370],"valid"],[[11371,11371],"mapped",[11372]],[[11372,11372],"valid"],[[11373,11373],"mapped",[593]],[[11374,11374],"mapped",[625]],[[11375,11375],"mapped",[592]],[[11376,11376],"mapped",[594]],[[11377,11377],"valid"],[[11378,11378],"mapped",[11379]],[[11379,11379],"valid"],[[11380,11380],"valid"],[[11381,11381],"mapped",[11382]],[[11382,11383],"valid"],[[11384,11387],"valid"],[[11388,11388],"mapped",[106]],[[11389,11389],"mapped",[118]],[[11390,11390],"mapped",[575]],[[11391,11391],"mapped",[576]],[[11392,11392],"mapped",[11393]],[[11393,11393],"valid"],[[11394,11394],"mapped",[11395]],[[11395,11395],"valid"],[[11396,11396],"mapped",[11397]],[[11397,11397],"valid"],[[11398,11398],"mapped",[11399]],[[11399,11399],"valid"],[[11400,11400],"mapped",[11401]],[[11401,11401],"valid"],[[11402,11402],"mapped",[11403]],[[11403,11403],"valid"],[[11404,11404],"mapped",[11405]],[[11405,11405],"valid"],[[11406,11406],"mapped",[11407]],[[11407,11407],"valid"],[[11408,11408],"mapped",[11409]],[[11409,11409],"valid"],[[11410,11410],"mapped",[11411]],[[11411,11411],"valid"],[[11412,11412],"mapped",[11413]],[[11413,11413],"valid"],[[11414,11414],"mapped",[11415]],[[11415,11415],"valid"],[[11416,11416],"mapped",[11417]],[[11417,11417],"valid"],[[11418,11418],"mapped",[11419]],[[11419,11419],"valid"],[[11420,11420],"mapped",[11421]],[[11421,11421],"valid"],[[11422,11422],"mapped",[11423]],[[11423,11423],"valid"],[[11424,11424],"mapped",[11425]],[[11425,11425],"valid"],[[11426,11426],"mapped",[11427]],[[11427,11427],"valid"],[[11428,11428],"mapped",[11429]],[[11429,11429],"valid"],[[11430,11430],"mapped",[11431]],[[11431,11431],"valid"],[[11432,11432],"mapped",[11433]],[[11433,11433],"valid"],[[11434,11434],"mapped",[11435]],[[11435,11435],"valid"],[[11436,11436],"mapped",[11437]],[[11437,11437],"valid"],[[11438,11438],"mapped",[11439]],[[11439,11439],"valid"],[[11440,11440],"mapped",[11441]],[[11441,11441],"valid"],[[11442,11442],"mapped",[11443]],[[11443,11443],"valid"],[[11444,11444],"mapped",[11445]],[[11445,11445],"valid"],[[11446,11446],"mapped",[11447]],[[11447,11447],"valid"],[[11448,11448],"mapped",[11449]],[[11449,11449],"valid"],[[11450,11450],"mapped",[11451]],[[11451,11451],"valid"],[[11452,11452],"mapped",[11453]],[[11453,11453],"valid"],[[11454,11454],"mapped",[11455]],[[11455,11455],"valid"],[[11456,11456],"mapped",[11457]],[[11457,11457],"valid"],[[11458,11458],"mapped",[11459]],[[11459,11459],"valid"],[[11460,11460],"mapped",[11461]],[[11461,11461],"valid"],[[11462,11462],"mapped",[11463]],[[11463,11463],"valid"],[[11464,11464],"mapped",[11465]],[[11465,11465],"valid"],[[11466,11466],"mapped",[11467]],[[11467,11467],"valid"],[[11468,11468],"mapped",[11469]],[[11469,11469],"valid"],[[11470,11470],"mapped",[11471]],[[11471,11471],"valid"],[[11472,11472],"mapped",[11473]],[[11473,11473],"valid"],[[11474,11474],"mapped",[11475]],[[11475,11475],"valid"],[[11476,11476],"mapped",[11477]],[[11477,11477],"valid"],[[11478,11478],"mapped",[11479]],[[11479,11479],"valid"],[[11480,11480],"mapped",[11481]],[[11481,11481],"valid"],[[11482,11482],"mapped",[11483]],[[11483,11483],"valid"],[[11484,11484],"mapped",[11485]],[[11485,11485],"valid"],[[11486,11486],"mapped",[11487]],[[11487,11487],"valid"],[[11488,11488],"mapped",[11489]],[[11489,11489],"valid"],[[11490,11490],"mapped",[11491]],[[11491,11492],"valid"],[[11493,11498],"valid",[],"NV8"],[[11499,11499],"mapped",[11500]],[[11500,11500],"valid"],[[11501,11501],"mapped",[11502]],[[11502,11505],"valid"],[[11506,11506],"mapped",[11507]],[[11507,11507],"valid"],[[11508,11512],"disallowed"],[[11513,11519],"valid",[],"NV8"],[[11520,11557],"valid"],[[11558,11558],"disallowed"],[[11559,11559],"valid"],[[11560,11564],"disallowed"],[[11565,11565],"valid"],[[11566,11567],"disallowed"],[[11568,11621],"valid"],[[11622,11623],"valid"],[[11624,11630],"disallowed"],[[11631,11631],"mapped",[11617]],[[11632,11632],"valid",[],"NV8"],[[11633,11646],"disallowed"],[[11647,11647],"valid"],[[11648,11670],"valid"],[[11671,11679],"disallowed"],[[11680,11686],"valid"],[[11687,11687],"disallowed"],[[11688,11694],"valid"],[[11695,11695],"disallowed"],[[11696,11702],"valid"],[[11703,11703],"disallowed"],[[11704,11710],"valid"],[[11711,11711],"disallowed"],[[11712,11718],"valid"],[[11719,11719],"disallowed"],[[11720,11726],"valid"],[[11727,11727],"disallowed"],[[11728,11734],"valid"],[[11735,11735],"disallowed"],[[11736,11742],"valid"],[[11743,11743],"disallowed"],[[11744,11775],"valid"],[[11776,11799],"valid",[],"NV8"],[[11800,11803],"valid",[],"NV8"],[[11804,11805],"valid",[],"NV8"],[[11806,11822],"valid",[],"NV8"],[[11823,11823],"valid"],[[11824,11824],"valid",[],"NV8"],[[11825,11825],"valid",[],"NV8"],[[11826,11835],"valid",[],"NV8"],[[11836,11842],"valid",[],"NV8"],[[11843,11903],"disallowed"],[[11904,11929],"valid",[],"NV8"],[[11930,11930],"disallowed"],[[11931,11934],"valid",[],"NV8"],[[11935,11935],"mapped",[27597]],[[11936,12018],"valid",[],"NV8"],[[12019,12019],"mapped",[40863]],[[12020,12031],"disallowed"],[[12032,12032],"mapped",[19968]],[[12033,12033],"mapped",[20008]],[[12034,12034],"mapped",[20022]],[[12035,12035],"mapped",[20031]],[[12036,12036],"mapped",[20057]],[[12037,12037],"mapped",[20101]],[[12038,12038],"mapped",[20108]],[[12039,12039],"mapped",[20128]],[[12040,12040],"mapped",[20154]],[[12041,12041],"mapped",[20799]],[[12042,12042],"mapped",[20837]],[[12043,12043],"mapped",[20843]],[[12044,12044],"mapped",[20866]],[[12045,12045],"mapped",[20886]],[[12046,12046],"mapped",[20907]],[[12047,12047],"mapped",[20960]],[[12048,12048],"mapped",[20981]],[[12049,12049],"mapped",[20992]],[[12050,12050],"mapped",[21147]],[[12051,12051],"mapped",[21241]],[[12052,12052],"mapped",[21269]],[[12053,12053],"mapped",[21274]],[[12054,12054],"mapped",[21304]],[[12055,12055],"mapped",[21313]],[[12056,12056],"mapped",[21340]],[[12057,12057],"mapped",[21353]],[[12058,12058],"mapped",[21378]],[[12059,12059],"mapped",[21430]],[[12060,12060],"mapped",[21448]],[[12061,12061],"mapped",[21475]],[[12062,12062],"mapped",[22231]],[[12063,12063],"mapped",[22303]],[[12064,12064],"mapped",[22763]],[[12065,12065],"mapped",[22786]],[[12066,12066],"mapped",[22794]],[[12067,12067],"mapped",[22805]],[[12068,12068],"mapped",[22823]],[[12069,12069],"mapped",[22899]],[[12070,12070],"mapped",[23376]],[[12071,12071],"mapped",[23424]],[[12072,12072],"mapped",[23544]],[[12073,12073],"mapped",[23567]],[[12074,12074],"mapped",[23586]],[[12075,12075],"mapped",[23608]],[[12076,12076],"mapped",[23662]],[[12077,12077],"mapped",[23665]],[[12078,12078],"mapped",[24027]],[[12079,12079],"mapped",[24037]],[[12080,12080],"mapped",[24049]],[[12081,12081],"mapped",[24062]],[[12082,12082],"mapped",[24178]],[[12083,12083],"mapped",[24186]],[[12084,12084],"mapped",[24191]],[[12085,12085],"mapped",[24308]],[[12086,12086],"mapped",[24318]],[[12087,12087],"mapped",[24331]],[[12088,12088],"mapped",[24339]],[[12089,12089],"mapped",[24400]],[[12090,12090],"mapped",[24417]],[[12091,12091],"mapped",[24435]],[[12092,12092],"mapped",[24515]],[[12093,12093],"mapped",[25096]],[[12094,12094],"mapped",[25142]],[[12095,12095],"mapped",[25163]],[[12096,12096],"mapped",[25903]],[[12097,12097],"mapped",[25908]],[[12098,12098],"mapped",[25991]],[[12099,12099],"mapped",[26007]],[[12100,12100],"mapped",[26020]],[[12101,12101],"mapped",[26041]],[[12102,12102],"mapped",[26080]],[[12103,12103],"mapped",[26085]],[[12104,12104],"mapped",[26352]],[[12105,12105],"mapped",[26376]],[[12106,12106],"mapped",[26408]],[[12107,12107],"mapped",[27424]],[[12108,12108],"mapped",[27490]],[[12109,12109],"mapped",[27513]],[[12110,12110],"mapped",[27571]],[[12111,12111],"mapped",[27595]],[[12112,12112],"mapped",[27604]],[[12113,12113],"mapped",[27611]],[[12114,12114],"mapped",[27663]],[[12115,12115],"mapped",[27668]],[[12116,12116],"mapped",[27700]],[[12117,12117],"mapped",[28779]],[[12118,12118],"mapped",[29226]],[[12119,12119],"mapped",[29238]],[[12120,12120],"mapped",[29243]],[[12121,12121],"mapped",[29247]],[[12122,12122],"mapped",[29255]],[[12123,12123],"mapped",[29273]],[[12124,12124],"mapped",[29275]],[[12125,12125],"mapped",[29356]],[[12126,12126],"mapped",[29572]],[[12127,12127],"mapped",[29577]],[[12128,12128],"mapped",[29916]],[[12129,12129],"mapped",[29926]],[[12130,12130],"mapped",[29976]],[[12131,12131],"mapped",[29983]],[[12132,12132],"mapped",[29992]],[[12133,12133],"mapped",[30000]],[[12134,12134],"mapped",[30091]],[[12135,12135],"mapped",[30098]],[[12136,12136],"mapped",[30326]],[[12137,12137],"mapped",[30333]],[[12138,12138],"mapped",[30382]],[[12139,12139],"mapped",[30399]],[[12140,12140],"mapped",[30446]],[[12141,12141],"mapped",[30683]],[[12142,12142],"mapped",[30690]],[[12143,12143],"mapped",[30707]],[[12144,12144],"mapped",[31034]],[[12145,12145],"mapped",[31160]],[[12146,12146],"mapped",[31166]],[[12147,12147],"mapped",[31348]],[[12148,12148],"mapped",[31435]],[[12149,12149],"mapped",[31481]],[[12150,12150],"mapped",[31859]],[[12151,12151],"mapped",[31992]],[[12152,12152],"mapped",[32566]],[[12153,12153],"mapped",[32593]],[[12154,12154],"mapped",[32650]],[[12155,12155],"mapped",[32701]],[[12156,12156],"mapped",[32769]],[[12157,12157],"mapped",[32780]],[[12158,12158],"mapped",[32786]],[[12159,12159],"mapped",[32819]],[[12160,12160],"mapped",[32895]],[[12161,12161],"mapped",[32905]],[[12162,12162],"mapped",[33251]],[[12163,12163],"mapped",[33258]],[[12164,12164],"mapped",[33267]],[[12165,12165],"mapped",[33276]],[[12166,12166],"mapped",[33292]],[[12167,12167],"mapped",[33307]],[[12168,12168],"mapped",[33311]],[[12169,12169],"mapped",[33390]],[[12170,12170],"mapped",[33394]],[[12171,12171],"mapped",[33400]],[[12172,12172],"mapped",[34381]],[[12173,12173],"mapped",[34411]],[[12174,12174],"mapped",[34880]],[[12175,12175],"mapped",[34892]],[[12176,12176],"mapped",[34915]],[[12177,12177],"mapped",[35198]],[[12178,12178],"mapped",[35211]],[[12179,12179],"mapped",[35282]],[[12180,12180],"mapped",[35328]],[[12181,12181],"mapped",[35895]],[[12182,12182],"mapped",[35910]],[[12183,12183],"mapped",[35925]],[[12184,12184],"mapped",[35960]],[[12185,12185],"mapped",[35997]],[[12186,12186],"mapped",[36196]],[[12187,12187],"mapped",[36208]],[[12188,12188],"mapped",[36275]],[[12189,12189],"mapped",[36523]],[[12190,12190],"mapped",[36554]],[[12191,12191],"mapped",[36763]],[[12192,12192],"mapped",[36784]],[[12193,12193],"mapped",[36789]],[[12194,12194],"mapped",[37009]],[[12195,12195],"mapped",[37193]],[[12196,12196],"mapped",[37318]],[[12197,12197],"mapped",[37324]],[[12198,12198],"mapped",[37329]],[[12199,12199],"mapped",[38263]],[[12200,12200],"mapped",[38272]],[[12201,12201],"mapped",[38428]],[[12202,12202],"mapped",[38582]],[[12203,12203],"mapped",[38585]],[[12204,12204],"mapped",[38632]],[[12205,12205],"mapped",[38737]],[[12206,12206],"mapped",[38750]],[[12207,12207],"mapped",[38754]],[[12208,12208],"mapped",[38761]],[[12209,12209],"mapped",[38859]],[[12210,12210],"mapped",[38893]],[[12211,12211],"mapped",[38899]],[[12212,12212],"mapped",[38913]],[[12213,12213],"mapped",[39080]],[[12214,12214],"mapped",[39131]],[[12215,12215],"mapped",[39135]],[[12216,12216],"mapped",[39318]],[[12217,12217],"mapped",[39321]],[[12218,12218],"mapped",[39340]],[[12219,12219],"mapped",[39592]],[[12220,12220],"mapped",[39640]],[[12221,12221],"mapped",[39647]],[[12222,12222],"mapped",[39717]],[[12223,12223],"mapped",[39727]],[[12224,12224],"mapped",[39730]],[[12225,12225],"mapped",[39740]],[[12226,12226],"mapped",[39770]],[[12227,12227],"mapped",[40165]],[[12228,12228],"mapped",[40565]],[[12229,12229],"mapped",[40575]],[[12230,12230],"mapped",[40613]],[[12231,12231],"mapped",[40635]],[[12232,12232],"mapped",[40643]],[[12233,12233],"mapped",[40653]],[[12234,12234],"mapped",[40657]],[[12235,12235],"mapped",[40697]],[[12236,12236],"mapped",[40701]],[[12237,12237],"mapped",[40718]],[[12238,12238],"mapped",[40723]],[[12239,12239],"mapped",[40736]],[[12240,12240],"mapped",[40763]],[[12241,12241],"mapped",[40778]],[[12242,12242],"mapped",[40786]],[[12243,12243],"mapped",[40845]],[[12244,12244],"mapped",[40860]],[[12245,12245],"mapped",[40864]],[[12246,12271],"disallowed"],[[12272,12283],"disallowed"],[[12284,12287],"disallowed"],[[12288,12288],"disallowed_STD3_mapped",[32]],[[12289,12289],"valid",[],"NV8"],[[12290,12290],"mapped",[46]],[[12291,12292],"valid",[],"NV8"],[[12293,12295],"valid"],[[12296,12329],"valid",[],"NV8"],[[12330,12333],"valid"],[[12334,12341],"valid",[],"NV8"],[[12342,12342],"mapped",[12306]],[[12343,12343],"valid",[],"NV8"],[[12344,12344],"mapped",[21313]],[[12345,12345],"mapped",[21316]],[[12346,12346],"mapped",[21317]],[[12347,12347],"valid",[],"NV8"],[[12348,12348],"valid"],[[12349,12349],"valid",[],"NV8"],[[12350,12350],"valid",[],"NV8"],[[12351,12351],"valid",[],"NV8"],[[12352,12352],"disallowed"],[[12353,12436],"valid"],[[12437,12438],"valid"],[[12439,12440],"disallowed"],[[12441,12442],"valid"],[[12443,12443],"disallowed_STD3_mapped",[32,12441]],[[12444,12444],"disallowed_STD3_mapped",[32,12442]],[[12445,12446],"valid"],[[12447,12447],"mapped",[12424,12426]],[[12448,12448],"valid",[],"NV8"],[[12449,12542],"valid"],[[12543,12543],"mapped",[12467,12488]],[[12544,12548],"disallowed"],[[12549,12588],"valid"],[[12589,12589],"valid"],[[12590,12592],"disallowed"],[[12593,12593],"mapped",[4352]],[[12594,12594],"mapped",[4353]],[[12595,12595],"mapped",[4522]],[[12596,12596],"mapped",[4354]],[[12597,12597],"mapped",[4524]],[[12598,12598],"mapped",[4525]],[[12599,12599],"mapped",[4355]],[[12600,12600],"mapped",[4356]],[[12601,12601],"mapped",[4357]],[[12602,12602],"mapped",[4528]],[[12603,12603],"mapped",[4529]],[[12604,12604],"mapped",[4530]],[[12605,12605],"mapped",[4531]],[[12606,12606],"mapped",[4532]],[[12607,12607],"mapped",[4533]],[[12608,12608],"mapped",[4378]],[[12609,12609],"mapped",[4358]],[[12610,12610],"mapped",[4359]],[[12611,12611],"mapped",[4360]],[[12612,12612],"mapped",[4385]],[[12613,12613],"mapped",[4361]],[[12614,12614],"mapped",[4362]],[[12615,12615],"mapped",[4363]],[[12616,12616],"mapped",[4364]],[[12617,12617],"mapped",[4365]],[[12618,12618],"mapped",[4366]],[[12619,12619],"mapped",[4367]],[[12620,12620],"mapped",[4368]],[[12621,12621],"mapped",[4369]],[[12622,12622],"mapped",[4370]],[[12623,12623],"mapped",[4449]],[[12624,12624],"mapped",[4450]],[[12625,12625],"mapped",[4451]],[[12626,12626],"mapped",[4452]],[[12627,12627],"mapped",[4453]],[[12628,12628],"mapped",[4454]],[[12629,12629],"mapped",[4455]],[[12630,12630],"mapped",[4456]],[[12631,12631],"mapped",[4457]],[[12632,12632],"mapped",[4458]],[[12633,12633],"mapped",[4459]],[[12634,12634],"mapped",[4460]],[[12635,12635],"mapped",[4461]],[[12636,12636],"mapped",[4462]],[[12637,12637],"mapped",[4463]],[[12638,12638],"mapped",[4464]],[[12639,12639],"mapped",[4465]],[[12640,12640],"mapped",[4466]],[[12641,12641],"mapped",[4467]],[[12642,12642],"mapped",[4468]],[[12643,12643],"mapped",[4469]],[[12644,12644],"disallowed"],[[12645,12645],"mapped",[4372]],[[12646,12646],"mapped",[4373]],[[12647,12647],"mapped",[4551]],[[12648,12648],"mapped",[4552]],[[12649,12649],"mapped",[4556]],[[12650,12650],"mapped",[4558]],[[12651,12651],"mapped",[4563]],[[12652,12652],"mapped",[4567]],[[12653,12653],"mapped",[4569]],[[12654,12654],"mapped",[4380]],[[12655,12655],"mapped",[4573]],[[12656,12656],"mapped",[4575]],[[12657,12657],"mapped",[4381]],[[12658,12658],"mapped",[4382]],[[12659,12659],"mapped",[4384]],[[12660,12660],"mapped",[4386]],[[12661,12661],"mapped",[4387]],[[12662,12662],"mapped",[4391]],[[12663,12663],"mapped",[4393]],[[12664,12664],"mapped",[4395]],[[12665,12665],"mapped",[4396]],[[12666,12666],"mapped",[4397]],[[12667,12667],"mapped",[4398]],[[12668,12668],"mapped",[4399]],[[12669,12669],"mapped",[4402]],[[12670,12670],"mapped",[4406]],[[12671,12671],"mapped",[4416]],[[12672,12672],"mapped",[4423]],[[12673,12673],"mapped",[4428]],[[12674,12674],"mapped",[4593]],[[12675,12675],"mapped",[4594]],[[12676,12676],"mapped",[4439]],[[12677,12677],"mapped",[4440]],[[12678,12678],"mapped",[4441]],[[12679,12679],"mapped",[4484]],[[12680,12680],"mapped",[4485]],[[12681,12681],"mapped",[4488]],[[12682,12682],"mapped",[4497]],[[12683,12683],"mapped",[4498]],[[12684,12684],"mapped",[4500]],[[12685,12685],"mapped",[4510]],[[12686,12686],"mapped",[4513]],[[12687,12687],"disallowed"],[[12688,12689],"valid",[],"NV8"],[[12690,12690],"mapped",[19968]],[[12691,12691],"mapped",[20108]],[[12692,12692],"mapped",[19977]],[[12693,12693],"mapped",[22235]],[[12694,12694],"mapped",[19978]],[[12695,12695],"mapped",[20013]],[[12696,12696],"mapped",[19979]],[[12697,12697],"mapped",[30002]],[[12698,12698],"mapped",[20057]],[[12699,12699],"mapped",[19993]],[[12700,12700],"mapped",[19969]],[[12701,12701],"mapped",[22825]],[[12702,12702],"mapped",[22320]],[[12703,12703],"mapped",[20154]],[[12704,12727],"valid"],[[12728,12730],"valid"],[[12731,12735],"disallowed"],[[12736,12751],"valid",[],"NV8"],[[12752,12771],"valid",[],"NV8"],[[12772,12783],"disallowed"],[[12784,12799],"valid"],[[12800,12800],"disallowed_STD3_mapped",[40,4352,41]],[[12801,12801],"disallowed_STD3_mapped",[40,4354,41]],[[12802,12802],"disallowed_STD3_mapped",[40,4355,41]],[[12803,12803],"disallowed_STD3_mapped",[40,4357,41]],[[12804,12804],"disallowed_STD3_mapped",[40,4358,41]],[[12805,12805],"disallowed_STD3_mapped",[40,4359,41]],[[12806,12806],"disallowed_STD3_mapped",[40,4361,41]],[[12807,12807],"disallowed_STD3_mapped",[40,4363,41]],[[12808,12808],"disallowed_STD3_mapped",[40,4364,41]],[[12809,12809],"disallowed_STD3_mapped",[40,4366,41]],[[12810,12810],"disallowed_STD3_mapped",[40,4367,41]],[[12811,12811],"disallowed_STD3_mapped",[40,4368,41]],[[12812,12812],"disallowed_STD3_mapped",[40,4369,41]],[[12813,12813],"disallowed_STD3_mapped",[40,4370,41]],[[12814,12814],"disallowed_STD3_mapped",[40,44032,41]],[[12815,12815],"disallowed_STD3_mapped",[40,45208,41]],[[12816,12816],"disallowed_STD3_mapped",[40,45796,41]],[[12817,12817],"disallowed_STD3_mapped",[40,46972,41]],[[12818,12818],"disallowed_STD3_mapped",[40,47560,41]],[[12819,12819],"disallowed_STD3_mapped",[40,48148,41]],[[12820,12820],"disallowed_STD3_mapped",[40,49324,41]],[[12821,12821],"disallowed_STD3_mapped",[40,50500,41]],[[12822,12822],"disallowed_STD3_mapped",[40,51088,41]],[[12823,12823],"disallowed_STD3_mapped",[40,52264,41]],[[12824,12824],"disallowed_STD3_mapped",[40,52852,41]],[[12825,12825],"disallowed_STD3_mapped",[40,53440,41]],[[12826,12826],"disallowed_STD3_mapped",[40,54028,41]],[[12827,12827],"disallowed_STD3_mapped",[40,54616,41]],[[12828,12828],"disallowed_STD3_mapped",[40,51452,41]],[[12829,12829],"disallowed_STD3_mapped",[40,50724,51204,41]],[[12830,12830],"disallowed_STD3_mapped",[40,50724,54980,41]],[[12831,12831],"disallowed"],[[12832,12832],"disallowed_STD3_mapped",[40,19968,41]],[[12833,12833],"disallowed_STD3_mapped",[40,20108,41]],[[12834,12834],"disallowed_STD3_mapped",[40,19977,41]],[[12835,12835],"disallowed_STD3_mapped",[40,22235,41]],[[12836,12836],"disallowed_STD3_mapped",[40,20116,41]],[[12837,12837],"disallowed_STD3_mapped",[40,20845,41]],[[12838,12838],"disallowed_STD3_mapped",[40,19971,41]],[[12839,12839],"disallowed_STD3_mapped",[40,20843,41]],[[12840,12840],"disallowed_STD3_mapped",[40,20061,41]],[[12841,12841],"disallowed_STD3_mapped",[40,21313,41]],[[12842,12842],"disallowed_STD3_mapped",[40,26376,41]],[[12843,12843],"disallowed_STD3_mapped",[40,28779,41]],[[12844,12844],"disallowed_STD3_mapped",[40,27700,41]],[[12845,12845],"disallowed_STD3_mapped",[40,26408,41]],[[12846,12846],"disallowed_STD3_mapped",[40,37329,41]],[[12847,12847],"disallowed_STD3_mapped",[40,22303,41]],[[12848,12848],"disallowed_STD3_mapped",[40,26085,41]],[[12849,12849],"disallowed_STD3_mapped",[40,26666,41]],[[12850,12850],"disallowed_STD3_mapped",[40,26377,41]],[[12851,12851],"disallowed_STD3_mapped",[40,31038,41]],[[12852,12852],"disallowed_STD3_mapped",[40,21517,41]],[[12853,12853],"disallowed_STD3_mapped",[40,29305,41]],[[12854,12854],"disallowed_STD3_mapped",[40,36001,41]],[[12855,12855],"disallowed_STD3_mapped",[40,31069,41]],[[12856,12856],"disallowed_STD3_mapped",[40,21172,41]],[[12857,12857],"disallowed_STD3_mapped",[40,20195,41]],[[12858,12858],"disallowed_STD3_mapped",[40,21628,41]],[[12859,12859],"disallowed_STD3_mapped",[40,23398,41]],[[12860,12860],"disallowed_STD3_mapped",[40,30435,41]],[[12861,12861],"disallowed_STD3_mapped",[40,20225,41]],[[12862,12862],"disallowed_STD3_mapped",[40,36039,41]],[[12863,12863],"disallowed_STD3_mapped",[40,21332,41]],[[12864,12864],"disallowed_STD3_mapped",[40,31085,41]],[[12865,12865],"disallowed_STD3_mapped",[40,20241,41]],[[12866,12866],"disallowed_STD3_mapped",[40,33258,41]],[[12867,12867],"disallowed_STD3_mapped",[40,33267,41]],[[12868,12868],"mapped",[21839]],[[12869,12869],"mapped",[24188]],[[12870,12870],"mapped",[25991]],[[12871,12871],"mapped",[31631]],[[12872,12879],"valid",[],"NV8"],[[12880,12880],"mapped",[112,116,101]],[[12881,12881],"mapped",[50,49]],[[12882,12882],"mapped",[50,50]],[[12883,12883],"mapped",[50,51]],[[12884,12884],"mapped",[50,52]],[[12885,12885],"mapped",[50,53]],[[12886,12886],"mapped",[50,54]],[[12887,12887],"mapped",[50,55]],[[12888,12888],"mapped",[50,56]],[[12889,12889],"mapped",[50,57]],[[12890,12890],"mapped",[51,48]],[[12891,12891],"mapped",[51,49]],[[12892,12892],"mapped",[51,50]],[[12893,12893],"mapped",[51,51]],[[12894,12894],"mapped",[51,52]],[[12895,12895],"mapped",[51,53]],[[12896,12896],"mapped",[4352]],[[12897,12897],"mapped",[4354]],[[12898,12898],"mapped",[4355]],[[12899,12899],"mapped",[4357]],[[12900,12900],"mapped",[4358]],[[12901,12901],"mapped",[4359]],[[12902,12902],"mapped",[4361]],[[12903,12903],"mapped",[4363]],[[12904,12904],"mapped",[4364]],[[12905,12905],"mapped",[4366]],[[12906,12906],"mapped",[4367]],[[12907,12907],"mapped",[4368]],[[12908,12908],"mapped",[4369]],[[12909,12909],"mapped",[4370]],[[12910,12910],"mapped",[44032]],[[12911,12911],"mapped",[45208]],[[12912,12912],"mapped",[45796]],[[12913,12913],"mapped",[46972]],[[12914,12914],"mapped",[47560]],[[12915,12915],"mapped",[48148]],[[12916,12916],"mapped",[49324]],[[12917,12917],"mapped",[50500]],[[12918,12918],"mapped",[51088]],[[12919,12919],"mapped",[52264]],[[12920,12920],"mapped",[52852]],[[12921,12921],"mapped",[53440]],[[12922,12922],"mapped",[54028]],[[12923,12923],"mapped",[54616]],[[12924,12924],"mapped",[52280,44256]],[[12925,12925],"mapped",[51452,51032]],[[12926,12926],"mapped",[50864]],[[12927,12927],"valid",[],"NV8"],[[12928,12928],"mapped",[19968]],[[12929,12929],"mapped",[20108]],[[12930,12930],"mapped",[19977]],[[12931,12931],"mapped",[22235]],[[12932,12932],"mapped",[20116]],[[12933,12933],"mapped",[20845]],[[12934,12934],"mapped",[19971]],[[12935,12935],"mapped",[20843]],[[12936,12936],"mapped",[20061]],[[12937,12937],"mapped",[21313]],[[12938,12938],"mapped",[26376]],[[12939,12939],"mapped",[28779]],[[12940,12940],"mapped",[27700]],[[12941,12941],"mapped",[26408]],[[12942,12942],"mapped",[37329]],[[12943,12943],"mapped",[22303]],[[12944,12944],"mapped",[26085]],[[12945,12945],"mapped",[26666]],[[12946,12946],"mapped",[26377]],[[12947,12947],"mapped",[31038]],[[12948,12948],"mapped",[21517]],[[12949,12949],"mapped",[29305]],[[12950,12950],"mapped",[36001]],[[12951,12951],"mapped",[31069]],[[12952,12952],"mapped",[21172]],[[12953,12953],"mapped",[31192]],[[12954,12954],"mapped",[30007]],[[12955,12955],"mapped",[22899]],[[12956,12956],"mapped",[36969]],[[12957,12957],"mapped",[20778]],[[12958,12958],"mapped",[21360]],[[12959,12959],"mapped",[27880]],[[12960,12960],"mapped",[38917]],[[12961,12961],"mapped",[20241]],[[12962,12962],"mapped",[20889]],[[12963,12963],"mapped",[27491]],[[12964,12964],"mapped",[19978]],[[12965,12965],"mapped",[20013]],[[12966,12966],"mapped",[19979]],[[12967,12967],"mapped",[24038]],[[12968,12968],"mapped",[21491]],[[12969,12969],"mapped",[21307]],[[12970,12970],"mapped",[23447]],[[12971,12971],"mapped",[23398]],[[12972,12972],"mapped",[30435]],[[12973,12973],"mapped",[20225]],[[12974,12974],"mapped",[36039]],[[12975,12975],"mapped",[21332]],[[12976,12976],"mapped",[22812]],[[12977,12977],"mapped",[51,54]],[[12978,12978],"mapped",[51,55]],[[12979,12979],"mapped",[51,56]],[[12980,12980],"mapped",[51,57]],[[12981,12981],"mapped",[52,48]],[[12982,12982],"mapped",[52,49]],[[12983,12983],"mapped",[52,50]],[[12984,12984],"mapped",[52,51]],[[12985,12985],"mapped",[52,52]],[[12986,12986],"mapped",[52,53]],[[12987,12987],"mapped",[52,54]],[[12988,12988],"mapped",[52,55]],[[12989,12989],"mapped",[52,56]],[[12990,12990],"mapped",[52,57]],[[12991,12991],"mapped",[53,48]],[[12992,12992],"mapped",[49,26376]],[[12993,12993],"mapped",[50,26376]],[[12994,12994],"mapped",[51,26376]],[[12995,12995],"mapped",[52,26376]],[[12996,12996],"mapped",[53,26376]],[[12997,12997],"mapped",[54,26376]],[[12998,12998],"mapped",[55,26376]],[[12999,12999],"mapped",[56,26376]],[[13000,13000],"mapped",[57,26376]],[[13001,13001],"mapped",[49,48,26376]],[[13002,13002],"mapped",[49,49,26376]],[[13003,13003],"mapped",[49,50,26376]],[[13004,13004],"mapped",[104,103]],[[13005,13005],"mapped",[101,114,103]],[[13006,13006],"mapped",[101,118]],[[13007,13007],"mapped",[108,116,100]],[[13008,13008],"mapped",[12450]],[[13009,13009],"mapped",[12452]],[[13010,13010],"mapped",[12454]],[[13011,13011],"mapped",[12456]],[[13012,13012],"mapped",[12458]],[[13013,13013],"mapped",[12459]],[[13014,13014],"mapped",[12461]],[[13015,13015],"mapped",[12463]],[[13016,13016],"mapped",[12465]],[[13017,13017],"mapped",[12467]],[[13018,13018],"mapped",[12469]],[[13019,13019],"mapped",[12471]],[[13020,13020],"mapped",[12473]],[[13021,13021],"mapped",[12475]],[[13022,13022],"mapped",[12477]],[[13023,13023],"mapped",[12479]],[[13024,13024],"mapped",[12481]],[[13025,13025],"mapped",[12484]],[[13026,13026],"mapped",[12486]],[[13027,13027],"mapped",[12488]],[[13028,13028],"mapped",[12490]],[[13029,13029],"mapped",[12491]],[[13030,13030],"mapped",[12492]],[[13031,13031],"mapped",[12493]],[[13032,13032],"mapped",[12494]],[[13033,13033],"mapped",[12495]],[[13034,13034],"mapped",[12498]],[[13035,13035],"mapped",[12501]],[[13036,13036],"mapped",[12504]],[[13037,13037],"mapped",[12507]],[[13038,13038],"mapped",[12510]],[[13039,13039],"mapped",[12511]],[[13040,13040],"mapped",[12512]],[[13041,13041],"mapped",[12513]],[[13042,13042],"mapped",[12514]],[[13043,13043],"mapped",[12516]],[[13044,13044],"mapped",[12518]],[[13045,13045],"mapped",[12520]],[[13046,13046],"mapped",[12521]],[[13047,13047],"mapped",[12522]],[[13048,13048],"mapped",[12523]],[[13049,13049],"mapped",[12524]],[[13050,13050],"mapped",[12525]],[[13051,13051],"mapped",[12527]],[[13052,13052],"mapped",[12528]],[[13053,13053],"mapped",[12529]],[[13054,13054],"mapped",[12530]],[[13055,13055],"disallowed"],[[13056,13056],"mapped",[12450,12497,12540,12488]],[[13057,13057],"mapped",[12450,12523,12501,12449]],[[13058,13058],"mapped",[12450,12531,12506,12450]],[[13059,13059],"mapped",[12450,12540,12523]],[[13060,13060],"mapped",[12452,12491,12531,12464]],[[13061,13061],"mapped",[12452,12531,12481]],[[13062,13062],"mapped",[12454,12457,12531]],[[13063,13063],"mapped",[12456,12473,12463,12540,12489]],[[13064,13064],"mapped",[12456,12540,12459,12540]],[[13065,13065],"mapped",[12458,12531,12473]],[[13066,13066],"mapped",[12458,12540,12512]],[[13067,13067],"mapped",[12459,12452,12522]],[[13068,13068],"mapped",[12459,12521,12483,12488]],[[13069,13069],"mapped",[12459,12525,12522,12540]],[[13070,13070],"mapped",[12460,12525,12531]],[[13071,13071],"mapped",[12460,12531,12510]],[[13072,13072],"mapped",[12462,12460]],[[13073,13073],"mapped",[12462,12491,12540]],[[13074,13074],"mapped",[12461,12517,12522,12540]],[[13075,13075],"mapped",[12462,12523,12480,12540]],[[13076,13076],"mapped",[12461,12525]],[[13077,13077],"mapped",[12461,12525,12464,12521,12512]],[[13078,13078],"mapped",[12461,12525,12513,12540,12488,12523]],[[13079,13079],"mapped",[12461,12525,12527,12483,12488]],[[13080,13080],"mapped",[12464,12521,12512]],[[13081,13081],"mapped",[12464,12521,12512,12488,12531]],[[13082,13082],"mapped",[12463,12523,12476,12452,12525]],[[13083,13083],"mapped",[12463,12525,12540,12493]],[[13084,13084],"mapped",[12465,12540,12473]],[[13085,13085],"mapped",[12467,12523,12490]],[[13086,13086],"mapped",[12467,12540,12509]],[[13087,13087],"mapped",[12469,12452,12463,12523]],[[13088,13088],"mapped",[12469,12531,12481,12540,12512]],[[13089,13089],"mapped",[12471,12522,12531,12464]],[[13090,13090],"mapped",[12475,12531,12481]],[[13091,13091],"mapped",[12475,12531,12488]],[[13092,13092],"mapped",[12480,12540,12473]],[[13093,13093],"mapped",[12487,12471]],[[13094,13094],"mapped",[12489,12523]],[[13095,13095],"mapped",[12488,12531]],[[13096,13096],"mapped",[12490,12494]],[[13097,13097],"mapped",[12494,12483,12488]],[[13098,13098],"mapped",[12495,12452,12484]],[[13099,13099],"mapped",[12497,12540,12475,12531,12488]],[[13100,13100],"mapped",[12497,12540,12484]],[[13101,13101],"mapped",[12496,12540,12524,12523]],[[13102,13102],"mapped",[12500,12450,12473,12488,12523]],[[13103,13103],"mapped",[12500,12463,12523]],[[13104,13104],"mapped",[12500,12467]],[[13105,13105],"mapped",[12499,12523]],[[13106,13106],"mapped",[12501,12449,12521,12483,12489]],[[13107,13107],"mapped",[12501,12451,12540,12488]],[[13108,13108],"mapped",[12502,12483,12471,12455,12523]],[[13109,13109],"mapped",[12501,12521,12531]],[[13110,13110],"mapped",[12504,12463,12479,12540,12523]],[[13111,13111],"mapped",[12506,12477]],[[13112,13112],"mapped",[12506,12491,12498]],[[13113,13113],"mapped",[12504,12523,12484]],[[13114,13114],"mapped",[12506,12531,12473]],[[13115,13115],"mapped",[12506,12540,12472]],[[13116,13116],"mapped",[12505,12540,12479]],[[13117,13117],"mapped",[12509,12452,12531,12488]],[[13118,13118],"mapped",[12508,12523,12488]],[[13119,13119],"mapped",[12507,12531]],[[13120,13120],"mapped",[12509,12531,12489]],[[13121,13121],"mapped",[12507,12540,12523]],[[13122,13122],"mapped",[12507,12540,12531]],[[13123,13123],"mapped",[12510,12452,12463,12525]],[[13124,13124],"mapped",[12510,12452,12523]],[[13125,13125],"mapped",[12510,12483,12495]],[[13126,13126],"mapped",[12510,12523,12463]],[[13127,13127],"mapped",[12510,12531,12471,12519,12531]],[[13128,13128],"mapped",[12511,12463,12525,12531]],[[13129,13129],"mapped",[12511,12522]],[[13130,13130],"mapped",[12511,12522,12496,12540,12523]],[[13131,13131],"mapped",[12513,12460]],[[13132,13132],"mapped",[12513,12460,12488,12531]],[[13133,13133],"mapped",[12513,12540,12488,12523]],[[13134,13134],"mapped",[12516,12540,12489]],[[13135,13135],"mapped",[12516,12540,12523]],[[13136,13136],"mapped",[12518,12450,12531]],[[13137,13137],"mapped",[12522,12483,12488,12523]],[[13138,13138],"mapped",[12522,12521]],[[13139,13139],"mapped",[12523,12500,12540]],[[13140,13140],"mapped",[12523,12540,12502,12523]],[[13141,13141],"mapped",[12524,12512]],[[13142,13142],"mapped",[12524,12531,12488,12466,12531]],[[13143,13143],"mapped",[12527,12483,12488]],[[13144,13144],"mapped",[48,28857]],[[13145,13145],"mapped",[49,28857]],[[13146,13146],"mapped",[50,28857]],[[13147,13147],"mapped",[51,28857]],[[13148,13148],"mapped",[52,28857]],[[13149,13149],"mapped",[53,28857]],[[13150,13150],"mapped",[54,28857]],[[13151,13151],"mapped",[55,28857]],[[13152,13152],"mapped",[56,28857]],[[13153,13153],"mapped",[57,28857]],[[13154,13154],"mapped",[49,48,28857]],[[13155,13155],"mapped",[49,49,28857]],[[13156,13156],"mapped",[49,50,28857]],[[13157,13157],"mapped",[49,51,28857]],[[13158,13158],"mapped",[49,52,28857]],[[13159,13159],"mapped",[49,53,28857]],[[13160,13160],"mapped",[49,54,28857]],[[13161,13161],"mapped",[49,55,28857]],[[13162,13162],"mapped",[49,56,28857]],[[13163,13163],"mapped",[49,57,28857]],[[13164,13164],"mapped",[50,48,28857]],[[13165,13165],"mapped",[50,49,28857]],[[13166,13166],"mapped",[50,50,28857]],[[13167,13167],"mapped",[50,51,28857]],[[13168,13168],"mapped",[50,52,28857]],[[13169,13169],"mapped",[104,112,97]],[[13170,13170],"mapped",[100,97]],[[13171,13171],"mapped",[97,117]],[[13172,13172],"mapped",[98,97,114]],[[13173,13173],"mapped",[111,118]],[[13174,13174],"mapped",[112,99]],[[13175,13175],"mapped",[100,109]],[[13176,13176],"mapped",[100,109,50]],[[13177,13177],"mapped",[100,109,51]],[[13178,13178],"mapped",[105,117]],[[13179,13179],"mapped",[24179,25104]],[[13180,13180],"mapped",[26157,21644]],[[13181,13181],"mapped",[22823,27491]],[[13182,13182],"mapped",[26126,27835]],[[13183,13183],"mapped",[26666,24335,20250,31038]],[[13184,13184],"mapped",[112,97]],[[13185,13185],"mapped",[110,97]],[[13186,13186],"mapped",[956,97]],[[13187,13187],"mapped",[109,97]],[[13188,13188],"mapped",[107,97]],[[13189,13189],"mapped",[107,98]],[[13190,13190],"mapped",[109,98]],[[13191,13191],"mapped",[103,98]],[[13192,13192],"mapped",[99,97,108]],[[13193,13193],"mapped",[107,99,97,108]],[[13194,13194],"mapped",[112,102]],[[13195,13195],"mapped",[110,102]],[[13196,13196],"mapped",[956,102]],[[13197,13197],"mapped",[956,103]],[[13198,13198],"mapped",[109,103]],[[13199,13199],"mapped",[107,103]],[[13200,13200],"mapped",[104,122]],[[13201,13201],"mapped",[107,104,122]],[[13202,13202],"mapped",[109,104,122]],[[13203,13203],"mapped",[103,104,122]],[[13204,13204],"mapped",[116,104,122]],[[13205,13205],"mapped",[956,108]],[[13206,13206],"mapped",[109,108]],[[13207,13207],"mapped",[100,108]],[[13208,13208],"mapped",[107,108]],[[13209,13209],"mapped",[102,109]],[[13210,13210],"mapped",[110,109]],[[13211,13211],"mapped",[956,109]],[[13212,13212],"mapped",[109,109]],[[13213,13213],"mapped",[99,109]],[[13214,13214],"mapped",[107,109]],[[13215,13215],"mapped",[109,109,50]],[[13216,13216],"mapped",[99,109,50]],[[13217,13217],"mapped",[109,50]],[[13218,13218],"mapped",[107,109,50]],[[13219,13219],"mapped",[109,109,51]],[[13220,13220],"mapped",[99,109,51]],[[13221,13221],"mapped",[109,51]],[[13222,13222],"mapped",[107,109,51]],[[13223,13223],"mapped",[109,8725,115]],[[13224,13224],"mapped",[109,8725,115,50]],[[13225,13225],"mapped",[112,97]],[[13226,13226],"mapped",[107,112,97]],[[13227,13227],"mapped",[109,112,97]],[[13228,13228],"mapped",[103,112,97]],[[13229,13229],"mapped",[114,97,100]],[[13230,13230],"mapped",[114,97,100,8725,115]],[[13231,13231],"mapped",[114,97,100,8725,115,50]],[[13232,13232],"mapped",[112,115]],[[13233,13233],"mapped",[110,115]],[[13234,13234],"mapped",[956,115]],[[13235,13235],"mapped",[109,115]],[[13236,13236],"mapped",[112,118]],[[13237,13237],"mapped",[110,118]],[[13238,13238],"mapped",[956,118]],[[13239,13239],"mapped",[109,118]],[[13240,13240],"mapped",[107,118]],[[13241,13241],"mapped",[109,118]],[[13242,13242],"mapped",[112,119]],[[13243,13243],"mapped",[110,119]],[[13244,13244],"mapped",[956,119]],[[13245,13245],"mapped",[109,119]],[[13246,13246],"mapped",[107,119]],[[13247,13247],"mapped",[109,119]],[[13248,13248],"mapped",[107,969]],[[13249,13249],"mapped",[109,969]],[[13250,13250],"disallowed"],[[13251,13251],"mapped",[98,113]],[[13252,13252],"mapped",[99,99]],[[13253,13253],"mapped",[99,100]],[[13254,13254],"mapped",[99,8725,107,103]],[[13255,13255],"disallowed"],[[13256,13256],"mapped",[100,98]],[[13257,13257],"mapped",[103,121]],[[13258,13258],"mapped",[104,97]],[[13259,13259],"mapped",[104,112]],[[13260,13260],"mapped",[105,110]],[[13261,13261],"mapped",[107,107]],[[13262,13262],"mapped",[107,109]],[[13263,13263],"mapped",[107,116]],[[13264,13264],"mapped",[108,109]],[[13265,13265],"mapped",[108,110]],[[13266,13266],"mapped",[108,111,103]],[[13267,13267],"mapped",[108,120]],[[13268,13268],"mapped",[109,98]],[[13269,13269],"mapped",[109,105,108]],[[13270,13270],"mapped",[109,111,108]],[[13271,13271],"mapped",[112,104]],[[13272,13272],"disallowed"],[[13273,13273],"mapped",[112,112,109]],[[13274,13274],"mapped",[112,114]],[[13275,13275],"mapped",[115,114]],[[13276,13276],"mapped",[115,118]],[[13277,13277],"mapped",[119,98]],[[13278,13278],"mapped",[118,8725,109]],[[13279,13279],"mapped",[97,8725,109]],[[13280,13280],"mapped",[49,26085]],[[13281,13281],"mapped",[50,26085]],[[13282,13282],"mapped",[51,26085]],[[13283,13283],"mapped",[52,26085]],[[13284,13284],"mapped",[53,26085]],[[13285,13285],"mapped",[54,26085]],[[13286,13286],"mapped",[55,26085]],[[13287,13287],"mapped",[56,26085]],[[13288,13288],"mapped",[57,26085]],[[13289,13289],"mapped",[49,48,26085]],[[13290,13290],"mapped",[49,49,26085]],[[13291,13291],"mapped",[49,50,26085]],[[13292,13292],"mapped",[49,51,26085]],[[13293,13293],"mapped",[49,52,26085]],[[13294,13294],"mapped",[49,53,26085]],[[13295,13295],"mapped",[49,54,26085]],[[13296,13296],"mapped",[49,55,26085]],[[13297,13297],"mapped",[49,56,26085]],[[13298,13298],"mapped",[49,57,26085]],[[13299,13299],"mapped",[50,48,26085]],[[13300,13300],"mapped",[50,49,26085]],[[13301,13301],"mapped",[50,50,26085]],[[13302,13302],"mapped",[50,51,26085]],[[13303,13303],"mapped",[50,52,26085]],[[13304,13304],"mapped",[50,53,26085]],[[13305,13305],"mapped",[50,54,26085]],[[13306,13306],"mapped",[50,55,26085]],[[13307,13307],"mapped",[50,56,26085]],[[13308,13308],"mapped",[50,57,26085]],[[13309,13309],"mapped",[51,48,26085]],[[13310,13310],"mapped",[51,49,26085]],[[13311,13311],"mapped",[103,97,108]],[[13312,19893],"valid"],[[19894,19903],"disallowed"],[[19904,19967],"valid",[],"NV8"],[[19968,40869],"valid"],[[40870,40891],"valid"],[[40892,40899],"valid"],[[40900,40907],"valid"],[[40908,40908],"valid"],[[40909,40917],"valid"],[[40918,40959],"disallowed"],[[40960,42124],"valid"],[[42125,42127],"disallowed"],[[42128,42145],"valid",[],"NV8"],[[42146,42147],"valid",[],"NV8"],[[42148,42163],"valid",[],"NV8"],[[42164,42164],"valid",[],"NV8"],[[42165,42176],"valid",[],"NV8"],[[42177,42177],"valid",[],"NV8"],[[42178,42180],"valid",[],"NV8"],[[42181,42181],"valid",[],"NV8"],[[42182,42182],"valid",[],"NV8"],[[42183,42191],"disallowed"],[[42192,42237],"valid"],[[42238,42239],"valid",[],"NV8"],[[42240,42508],"valid"],[[42509,42511],"valid",[],"NV8"],[[42512,42539],"valid"],[[42540,42559],"disallowed"],[[42560,42560],"mapped",[42561]],[[42561,42561],"valid"],[[42562,42562],"mapped",[42563]],[[42563,42563],"valid"],[[42564,42564],"mapped",[42565]],[[42565,42565],"valid"],[[42566,42566],"mapped",[42567]],[[42567,42567],"valid"],[[42568,42568],"mapped",[42569]],[[42569,42569],"valid"],[[42570,42570],"mapped",[42571]],[[42571,42571],"valid"],[[42572,42572],"mapped",[42573]],[[42573,42573],"valid"],[[42574,42574],"mapped",[42575]],[[42575,42575],"valid"],[[42576,42576],"mapped",[42577]],[[42577,42577],"valid"],[[42578,42578],"mapped",[42579]],[[42579,42579],"valid"],[[42580,42580],"mapped",[42581]],[[42581,42581],"valid"],[[42582,42582],"mapped",[42583]],[[42583,42583],"valid"],[[42584,42584],"mapped",[42585]],[[42585,42585],"valid"],[[42586,42586],"mapped",[42587]],[[42587,42587],"valid"],[[42588,42588],"mapped",[42589]],[[42589,42589],"valid"],[[42590,42590],"mapped",[42591]],[[42591,42591],"valid"],[[42592,42592],"mapped",[42593]],[[42593,42593],"valid"],[[42594,42594],"mapped",[42595]],[[42595,42595],"valid"],[[42596,42596],"mapped",[42597]],[[42597,42597],"valid"],[[42598,42598],"mapped",[42599]],[[42599,42599],"valid"],[[42600,42600],"mapped",[42601]],[[42601,42601],"valid"],[[42602,42602],"mapped",[42603]],[[42603,42603],"valid"],[[42604,42604],"mapped",[42605]],[[42605,42607],"valid"],[[42608,42611],"valid",[],"NV8"],[[42612,42619],"valid"],[[42620,42621],"valid"],[[42622,42622],"valid",[],"NV8"],[[42623,42623],"valid"],[[42624,42624],"mapped",[42625]],[[42625,42625],"valid"],[[42626,42626],"mapped",[42627]],[[42627,42627],"valid"],[[42628,42628],"mapped",[42629]],[[42629,42629],"valid"],[[42630,42630],"mapped",[42631]],[[42631,42631],"valid"],[[42632,42632],"mapped",[42633]],[[42633,42633],"valid"],[[42634,42634],"mapped",[42635]],[[42635,42635],"valid"],[[42636,42636],"mapped",[42637]],[[42637,42637],"valid"],[[42638,42638],"mapped",[42639]],[[42639,42639],"valid"],[[42640,42640],"mapped",[42641]],[[42641,42641],"valid"],[[42642,42642],"mapped",[42643]],[[42643,42643],"valid"],[[42644,42644],"mapped",[42645]],[[42645,42645],"valid"],[[42646,42646],"mapped",[42647]],[[42647,42647],"valid"],[[42648,42648],"mapped",[42649]],[[42649,42649],"valid"],[[42650,42650],"mapped",[42651]],[[42651,42651],"valid"],[[42652,42652],"mapped",[1098]],[[42653,42653],"mapped",[1100]],[[42654,42654],"valid"],[[42655,42655],"valid"],[[42656,42725],"valid"],[[42726,42735],"valid",[],"NV8"],[[42736,42737],"valid"],[[42738,42743],"valid",[],"NV8"],[[42744,42751],"disallowed"],[[42752,42774],"valid",[],"NV8"],[[42775,42778],"valid"],[[42779,42783],"valid"],[[42784,42785],"valid",[],"NV8"],[[42786,42786],"mapped",[42787]],[[42787,42787],"valid"],[[42788,42788],"mapped",[42789]],[[42789,42789],"valid"],[[42790,42790],"mapped",[42791]],[[42791,42791],"valid"],[[42792,42792],"mapped",[42793]],[[42793,42793],"valid"],[[42794,42794],"mapped",[42795]],[[42795,42795],"valid"],[[42796,42796],"mapped",[42797]],[[42797,42797],"valid"],[[42798,42798],"mapped",[42799]],[[42799,42801],"valid"],[[42802,42802],"mapped",[42803]],[[42803,42803],"valid"],[[42804,42804],"mapped",[42805]],[[42805,42805],"valid"],[[42806,42806],"mapped",[42807]],[[42807,42807],"valid"],[[42808,42808],"mapped",[42809]],[[42809,42809],"valid"],[[42810,42810],"mapped",[42811]],[[42811,42811],"valid"],[[42812,42812],"mapped",[42813]],[[42813,42813],"valid"],[[42814,42814],"mapped",[42815]],[[42815,42815],"valid"],[[42816,42816],"mapped",[42817]],[[42817,42817],"valid"],[[42818,42818],"mapped",[42819]],[[42819,42819],"valid"],[[42820,42820],"mapped",[42821]],[[42821,42821],"valid"],[[42822,42822],"mapped",[42823]],[[42823,42823],"valid"],[[42824,42824],"mapped",[42825]],[[42825,42825],"valid"],[[42826,42826],"mapped",[42827]],[[42827,42827],"valid"],[[42828,42828],"mapped",[42829]],[[42829,42829],"valid"],[[42830,42830],"mapped",[42831]],[[42831,42831],"valid"],[[42832,42832],"mapped",[42833]],[[42833,42833],"valid"],[[42834,42834],"mapped",[42835]],[[42835,42835],"valid"],[[42836,42836],"mapped",[42837]],[[42837,42837],"valid"],[[42838,42838],"mapped",[42839]],[[42839,42839],"valid"],[[42840,42840],"mapped",[42841]],[[42841,42841],"valid"],[[42842,42842],"mapped",[42843]],[[42843,42843],"valid"],[[42844,42844],"mapped",[42845]],[[42845,42845],"valid"],[[42846,42846],"mapped",[42847]],[[42847,42847],"valid"],[[42848,42848],"mapped",[42849]],[[42849,42849],"valid"],[[42850,42850],"mapped",[42851]],[[42851,42851],"valid"],[[42852,42852],"mapped",[42853]],[[42853,42853],"valid"],[[42854,42854],"mapped",[42855]],[[42855,42855],"valid"],[[42856,42856],"mapped",[42857]],[[42857,42857],"valid"],[[42858,42858],"mapped",[42859]],[[42859,42859],"valid"],[[42860,42860],"mapped",[42861]],[[42861,42861],"valid"],[[42862,42862],"mapped",[42863]],[[42863,42863],"valid"],[[42864,42864],"mapped",[42863]],[[42865,42872],"valid"],[[42873,42873],"mapped",[42874]],[[42874,42874],"valid"],[[42875,42875],"mapped",[42876]],[[42876,42876],"valid"],[[42877,42877],"mapped",[7545]],[[42878,42878],"mapped",[42879]],[[42879,42879],"valid"],[[42880,42880],"mapped",[42881]],[[42881,42881],"valid"],[[42882,42882],"mapped",[42883]],[[42883,42883],"valid"],[[42884,42884],"mapped",[42885]],[[42885,42885],"valid"],[[42886,42886],"mapped",[42887]],[[42887,42888],"valid"],[[42889,42890],"valid",[],"NV8"],[[42891,42891],"mapped",[42892]],[[42892,42892],"valid"],[[42893,42893],"mapped",[613]],[[42894,42894],"valid"],[[42895,42895],"valid"],[[42896,42896],"mapped",[42897]],[[42897,42897],"valid"],[[42898,42898],"mapped",[42899]],[[42899,42899],"valid"],[[42900,42901],"valid"],[[42902,42902],"mapped",[42903]],[[42903,42903],"valid"],[[42904,42904],"mapped",[42905]],[[42905,42905],"valid"],[[42906,42906],"mapped",[42907]],[[42907,42907],"valid"],[[42908,42908],"mapped",[42909]],[[42909,42909],"valid"],[[42910,42910],"mapped",[42911]],[[42911,42911],"valid"],[[42912,42912],"mapped",[42913]],[[42913,42913],"valid"],[[42914,42914],"mapped",[42915]],[[42915,42915],"valid"],[[42916,42916],"mapped",[42917]],[[42917,42917],"valid"],[[42918,42918],"mapped",[42919]],[[42919,42919],"valid"],[[42920,42920],"mapped",[42921]],[[42921,42921],"valid"],[[42922,42922],"mapped",[614]],[[42923,42923],"mapped",[604]],[[42924,42924],"mapped",[609]],[[42925,42925],"mapped",[620]],[[42926,42927],"disallowed"],[[42928,42928],"mapped",[670]],[[42929,42929],"mapped",[647]],[[42930,42930],"mapped",[669]],[[42931,42931],"mapped",[43859]],[[42932,42932],"mapped",[42933]],[[42933,42933],"valid"],[[42934,42934],"mapped",[42935]],[[42935,42935],"valid"],[[42936,42998],"disallowed"],[[42999,42999],"valid"],[[43000,43000],"mapped",[295]],[[43001,43001],"mapped",[339]],[[43002,43002],"valid"],[[43003,43007],"valid"],[[43008,43047],"valid"],[[43048,43051],"valid",[],"NV8"],[[43052,43055],"disallowed"],[[43056,43065],"valid",[],"NV8"],[[43066,43071],"disallowed"],[[43072,43123],"valid"],[[43124,43127],"valid",[],"NV8"],[[43128,43135],"disallowed"],[[43136,43204],"valid"],[[43205,43213],"disallowed"],[[43214,43215],"valid",[],"NV8"],[[43216,43225],"valid"],[[43226,43231],"disallowed"],[[43232,43255],"valid"],[[43256,43258],"valid",[],"NV8"],[[43259,43259],"valid"],[[43260,43260],"valid",[],"NV8"],[[43261,43261],"valid"],[[43262,43263],"disallowed"],[[43264,43309],"valid"],[[43310,43311],"valid",[],"NV8"],[[43312,43347],"valid"],[[43348,43358],"disallowed"],[[43359,43359],"valid",[],"NV8"],[[43360,43388],"valid",[],"NV8"],[[43389,43391],"disallowed"],[[43392,43456],"valid"],[[43457,43469],"valid",[],"NV8"],[[43470,43470],"disallowed"],[[43471,43481],"valid"],[[43482,43485],"disallowed"],[[43486,43487],"valid",[],"NV8"],[[43488,43518],"valid"],[[43519,43519],"disallowed"],[[43520,43574],"valid"],[[43575,43583],"disallowed"],[[43584,43597],"valid"],[[43598,43599],"disallowed"],[[43600,43609],"valid"],[[43610,43611],"disallowed"],[[43612,43615],"valid",[],"NV8"],[[43616,43638],"valid"],[[43639,43641],"valid",[],"NV8"],[[43642,43643],"valid"],[[43644,43647],"valid"],[[43648,43714],"valid"],[[43715,43738],"disallowed"],[[43739,43741],"valid"],[[43742,43743],"valid",[],"NV8"],[[43744,43759],"valid"],[[43760,43761],"valid",[],"NV8"],[[43762,43766],"valid"],[[43767,43776],"disallowed"],[[43777,43782],"valid"],[[43783,43784],"disallowed"],[[43785,43790],"valid"],[[43791,43792],"disallowed"],[[43793,43798],"valid"],[[43799,43807],"disallowed"],[[43808,43814],"valid"],[[43815,43815],"disallowed"],[[43816,43822],"valid"],[[43823,43823],"disallowed"],[[43824,43866],"valid"],[[43867,43867],"valid",[],"NV8"],[[43868,43868],"mapped",[42791]],[[43869,43869],"mapped",[43831]],[[43870,43870],"mapped",[619]],[[43871,43871],"mapped",[43858]],[[43872,43875],"valid"],[[43876,43877],"valid"],[[43878,43887],"disallowed"],[[43888,43888],"mapped",[5024]],[[43889,43889],"mapped",[5025]],[[43890,43890],"mapped",[5026]],[[43891,43891],"mapped",[5027]],[[43892,43892],"mapped",[5028]],[[43893,43893],"mapped",[5029]],[[43894,43894],"mapped",[5030]],[[43895,43895],"mapped",[5031]],[[43896,43896],"mapped",[5032]],[[43897,43897],"mapped",[5033]],[[43898,43898],"mapped",[5034]],[[43899,43899],"mapped",[5035]],[[43900,43900],"mapped",[5036]],[[43901,43901],"mapped",[5037]],[[43902,43902],"mapped",[5038]],[[43903,43903],"mapped",[5039]],[[43904,43904],"mapped",[5040]],[[43905,43905],"mapped",[5041]],[[43906,43906],"mapped",[5042]],[[43907,43907],"mapped",[5043]],[[43908,43908],"mapped",[5044]],[[43909,43909],"mapped",[5045]],[[43910,43910],"mapped",[5046]],[[43911,43911],"mapped",[5047]],[[43912,43912],"mapped",[5048]],[[43913,43913],"mapped",[5049]],[[43914,43914],"mapped",[5050]],[[43915,43915],"mapped",[5051]],[[43916,43916],"mapped",[5052]],[[43917,43917],"mapped",[5053]],[[43918,43918],"mapped",[5054]],[[43919,43919],"mapped",[5055]],[[43920,43920],"mapped",[5056]],[[43921,43921],"mapped",[5057]],[[43922,43922],"mapped",[5058]],[[43923,43923],"mapped",[5059]],[[43924,43924],"mapped",[5060]],[[43925,43925],"mapped",[5061]],[[43926,43926],"mapped",[5062]],[[43927,43927],"mapped",[5063]],[[43928,43928],"mapped",[5064]],[[43929,43929],"mapped",[5065]],[[43930,43930],"mapped",[5066]],[[43931,43931],"mapped",[5067]],[[43932,43932],"mapped",[5068]],[[43933,43933],"mapped",[5069]],[[43934,43934],"mapped",[5070]],[[43935,43935],"mapped",[5071]],[[43936,43936],"mapped",[5072]],[[43937,43937],"mapped",[5073]],[[43938,43938],"mapped",[5074]],[[43939,43939],"mapped",[5075]],[[43940,43940],"mapped",[5076]],[[43941,43941],"mapped",[5077]],[[43942,43942],"mapped",[5078]],[[43943,43943],"mapped",[5079]],[[43944,43944],"mapped",[5080]],[[43945,43945],"mapped",[5081]],[[43946,43946],"mapped",[5082]],[[43947,43947],"mapped",[5083]],[[43948,43948],"mapped",[5084]],[[43949,43949],"mapped",[5085]],[[43950,43950],"mapped",[5086]],[[43951,43951],"mapped",[5087]],[[43952,43952],"mapped",[5088]],[[43953,43953],"mapped",[5089]],[[43954,43954],"mapped",[5090]],[[43955,43955],"mapped",[5091]],[[43956,43956],"mapped",[5092]],[[43957,43957],"mapped",[5093]],[[43958,43958],"mapped",[5094]],[[43959,43959],"mapped",[5095]],[[43960,43960],"mapped",[5096]],[[43961,43961],"mapped",[5097]],[[43962,43962],"mapped",[5098]],[[43963,43963],"mapped",[5099]],[[43964,43964],"mapped",[5100]],[[43965,43965],"mapped",[5101]],[[43966,43966],"mapped",[5102]],[[43967,43967],"mapped",[5103]],[[43968,44010],"valid"],[[44011,44011],"valid",[],"NV8"],[[44012,44013],"valid"],[[44014,44015],"disallowed"],[[44016,44025],"valid"],[[44026,44031],"disallowed"],[[44032,55203],"valid"],[[55204,55215],"disallowed"],[[55216,55238],"valid",[],"NV8"],[[55239,55242],"disallowed"],[[55243,55291],"valid",[],"NV8"],[[55292,55295],"disallowed"],[[55296,57343],"disallowed"],[[57344,63743],"disallowed"],[[63744,63744],"mapped",[35912]],[[63745,63745],"mapped",[26356]],[[63746,63746],"mapped",[36554]],[[63747,63747],"mapped",[36040]],[[63748,63748],"mapped",[28369]],[[63749,63749],"mapped",[20018]],[[63750,63750],"mapped",[21477]],[[63751,63752],"mapped",[40860]],[[63753,63753],"mapped",[22865]],[[63754,63754],"mapped",[37329]],[[63755,63755],"mapped",[21895]],[[63756,63756],"mapped",[22856]],[[63757,63757],"mapped",[25078]],[[63758,63758],"mapped",[30313]],[[63759,63759],"mapped",[32645]],[[63760,63760],"mapped",[34367]],[[63761,63761],"mapped",[34746]],[[63762,63762],"mapped",[35064]],[[63763,63763],"mapped",[37007]],[[63764,63764],"mapped",[27138]],[[63765,63765],"mapped",[27931]],[[63766,63766],"mapped",[28889]],[[63767,63767],"mapped",[29662]],[[63768,63768],"mapped",[33853]],[[63769,63769],"mapped",[37226]],[[63770,63770],"mapped",[39409]],[[63771,63771],"mapped",[20098]],[[63772,63772],"mapped",[21365]],[[63773,63773],"mapped",[27396]],[[63774,63774],"mapped",[29211]],[[63775,63775],"mapped",[34349]],[[63776,63776],"mapped",[40478]],[[63777,63777],"mapped",[23888]],[[63778,63778],"mapped",[28651]],[[63779,63779],"mapped",[34253]],[[63780,63780],"mapped",[35172]],[[63781,63781],"mapped",[25289]],[[63782,63782],"mapped",[33240]],[[63783,63783],"mapped",[34847]],[[63784,63784],"mapped",[24266]],[[63785,63785],"mapped",[26391]],[[63786,63786],"mapped",[28010]],[[63787,63787],"mapped",[29436]],[[63788,63788],"mapped",[37070]],[[63789,63789],"mapped",[20358]],[[63790,63790],"mapped",[20919]],[[63791,63791],"mapped",[21214]],[[63792,63792],"mapped",[25796]],[[63793,63793],"mapped",[27347]],[[63794,63794],"mapped",[29200]],[[63795,63795],"mapped",[30439]],[[63796,63796],"mapped",[32769]],[[63797,63797],"mapped",[34310]],[[63798,63798],"mapped",[34396]],[[63799,63799],"mapped",[36335]],[[63800,63800],"mapped",[38706]],[[63801,63801],"mapped",[39791]],[[63802,63802],"mapped",[40442]],[[63803,63803],"mapped",[30860]],[[63804,63804],"mapped",[31103]],[[63805,63805],"mapped",[32160]],[[63806,63806],"mapped",[33737]],[[63807,63807],"mapped",[37636]],[[63808,63808],"mapped",[40575]],[[63809,63809],"mapped",[35542]],[[63810,63810],"mapped",[22751]],[[63811,63811],"mapped",[24324]],[[63812,63812],"mapped",[31840]],[[63813,63813],"mapped",[32894]],[[63814,63814],"mapped",[29282]],[[63815,63815],"mapped",[30922]],[[63816,63816],"mapped",[36034]],[[63817,63817],"mapped",[38647]],[[63818,63818],"mapped",[22744]],[[63819,63819],"mapped",[23650]],[[63820,63820],"mapped",[27155]],[[63821,63821],"mapped",[28122]],[[63822,63822],"mapped",[28431]],[[63823,63823],"mapped",[32047]],[[63824,63824],"mapped",[32311]],[[63825,63825],"mapped",[38475]],[[63826,63826],"mapped",[21202]],[[63827,63827],"mapped",[32907]],[[63828,63828],"mapped",[20956]],[[63829,63829],"mapped",[20940]],[[63830,63830],"mapped",[31260]],[[63831,63831],"mapped",[32190]],[[63832,63832],"mapped",[33777]],[[63833,63833],"mapped",[38517]],[[63834,63834],"mapped",[35712]],[[63835,63835],"mapped",[25295]],[[63836,63836],"mapped",[27138]],[[63837,63837],"mapped",[35582]],[[63838,63838],"mapped",[20025]],[[63839,63839],"mapped",[23527]],[[63840,63840],"mapped",[24594]],[[63841,63841],"mapped",[29575]],[[63842,63842],"mapped",[30064]],[[63843,63843],"mapped",[21271]],[[63844,63844],"mapped",[30971]],[[63845,63845],"mapped",[20415]],[[63846,63846],"mapped",[24489]],[[63847,63847],"mapped",[19981]],[[63848,63848],"mapped",[27852]],[[63849,63849],"mapped",[25976]],[[63850,63850],"mapped",[32034]],[[63851,63851],"mapped",[21443]],[[63852,63852],"mapped",[22622]],[[63853,63853],"mapped",[30465]],[[63854,63854],"mapped",[33865]],[[63855,63855],"mapped",[35498]],[[63856,63856],"mapped",[27578]],[[63857,63857],"mapped",[36784]],[[63858,63858],"mapped",[27784]],[[63859,63859],"mapped",[25342]],[[63860,63860],"mapped",[33509]],[[63861,63861],"mapped",[25504]],[[63862,63862],"mapped",[30053]],[[63863,63863],"mapped",[20142]],[[63864,63864],"mapped",[20841]],[[63865,63865],"mapped",[20937]],[[63866,63866],"mapped",[26753]],[[63867,63867],"mapped",[31975]],[[63868,63868],"mapped",[33391]],[[63869,63869],"mapped",[35538]],[[63870,63870],"mapped",[37327]],[[63871,63871],"mapped",[21237]],[[63872,63872],"mapped",[21570]],[[63873,63873],"mapped",[22899]],[[63874,63874],"mapped",[24300]],[[63875,63875],"mapped",[26053]],[[63876,63876],"mapped",[28670]],[[63877,63877],"mapped",[31018]],[[63878,63878],"mapped",[38317]],[[63879,63879],"mapped",[39530]],[[63880,63880],"mapped",[40599]],[[63881,63881],"mapped",[40654]],[[63882,63882],"mapped",[21147]],[[63883,63883],"mapped",[26310]],[[63884,63884],"mapped",[27511]],[[63885,63885],"mapped",[36706]],[[63886,63886],"mapped",[24180]],[[63887,63887],"mapped",[24976]],[[63888,63888],"mapped",[25088]],[[63889,63889],"mapped",[25754]],[[63890,63890],"mapped",[28451]],[[63891,63891],"mapped",[29001]],[[63892,63892],"mapped",[29833]],[[63893,63893],"mapped",[31178]],[[63894,63894],"mapped",[32244]],[[63895,63895],"mapped",[32879]],[[63896,63896],"mapped",[36646]],[[63897,63897],"mapped",[34030]],[[63898,63898],"mapped",[36899]],[[63899,63899],"mapped",[37706]],[[63900,63900],"mapped",[21015]],[[63901,63901],"mapped",[21155]],[[63902,63902],"mapped",[21693]],[[63903,63903],"mapped",[28872]],[[63904,63904],"mapped",[35010]],[[63905,63905],"mapped",[35498]],[[63906,63906],"mapped",[24265]],[[63907,63907],"mapped",[24565]],[[63908,63908],"mapped",[25467]],[[63909,63909],"mapped",[27566]],[[63910,63910],"mapped",[31806]],[[63911,63911],"mapped",[29557]],[[63912,63912],"mapped",[20196]],[[63913,63913],"mapped",[22265]],[[63914,63914],"mapped",[23527]],[[63915,63915],"mapped",[23994]],[[63916,63916],"mapped",[24604]],[[63917,63917],"mapped",[29618]],[[63918,63918],"mapped",[29801]],[[63919,63919],"mapped",[32666]],[[63920,63920],"mapped",[32838]],[[63921,63921],"mapped",[37428]],[[63922,63922],"mapped",[38646]],[[63923,63923],"mapped",[38728]],[[63924,63924],"mapped",[38936]],[[63925,63925],"mapped",[20363]],[[63926,63926],"mapped",[31150]],[[63927,63927],"mapped",[37300]],[[63928,63928],"mapped",[38584]],[[63929,63929],"mapped",[24801]],[[63930,63930],"mapped",[20102]],[[63931,63931],"mapped",[20698]],[[63932,63932],"mapped",[23534]],[[63933,63933],"mapped",[23615]],[[63934,63934],"mapped",[26009]],[[63935,63935],"mapped",[27138]],[[63936,63936],"mapped",[29134]],[[63937,63937],"mapped",[30274]],[[63938,63938],"mapped",[34044]],[[63939,63939],"mapped",[36988]],[[63940,63940],"mapped",[40845]],[[63941,63941],"mapped",[26248]],[[63942,63942],"mapped",[38446]],[[63943,63943],"mapped",[21129]],[[63944,63944],"mapped",[26491]],[[63945,63945],"mapped",[26611]],[[63946,63946],"mapped",[27969]],[[63947,63947],"mapped",[28316]],[[63948,63948],"mapped",[29705]],[[63949,63949],"mapped",[30041]],[[63950,63950],"mapped",[30827]],[[63951,63951],"mapped",[32016]],[[63952,63952],"mapped",[39006]],[[63953,63953],"mapped",[20845]],[[63954,63954],"mapped",[25134]],[[63955,63955],"mapped",[38520]],[[63956,63956],"mapped",[20523]],[[63957,63957],"mapped",[23833]],[[63958,63958],"mapped",[28138]],[[63959,63959],"mapped",[36650]],[[63960,63960],"mapped",[24459]],[[63961,63961],"mapped",[24900]],[[63962,63962],"mapped",[26647]],[[63963,63963],"mapped",[29575]],[[63964,63964],"mapped",[38534]],[[63965,63965],"mapped",[21033]],[[63966,63966],"mapped",[21519]],[[63967,63967],"mapped",[23653]],[[63968,63968],"mapped",[26131]],[[63969,63969],"mapped",[26446]],[[63970,63970],"mapped",[26792]],[[63971,63971],"mapped",[27877]],[[63972,63972],"mapped",[29702]],[[63973,63973],"mapped",[30178]],[[63974,63974],"mapped",[32633]],[[63975,63975],"mapped",[35023]],[[63976,63976],"mapped",[35041]],[[63977,63977],"mapped",[37324]],[[63978,63978],"mapped",[38626]],[[63979,63979],"mapped",[21311]],[[63980,63980],"mapped",[28346]],[[63981,63981],"mapped",[21533]],[[63982,63982],"mapped",[29136]],[[63983,63983],"mapped",[29848]],[[63984,63984],"mapped",[34298]],[[63985,63985],"mapped",[38563]],[[63986,63986],"mapped",[40023]],[[63987,63987],"mapped",[40607]],[[63988,63988],"mapped",[26519]],[[63989,63989],"mapped",[28107]],[[63990,63990],"mapped",[33256]],[[63991,63991],"mapped",[31435]],[[63992,63992],"mapped",[31520]],[[63993,63993],"mapped",[31890]],[[63994,63994],"mapped",[29376]],[[63995,63995],"mapped",[28825]],[[63996,63996],"mapped",[35672]],[[63997,63997],"mapped",[20160]],[[63998,63998],"mapped",[33590]],[[63999,63999],"mapped",[21050]],[[64000,64000],"mapped",[20999]],[[64001,64001],"mapped",[24230]],[[64002,64002],"mapped",[25299]],[[64003,64003],"mapped",[31958]],[[64004,64004],"mapped",[23429]],[[64005,64005],"mapped",[27934]],[[64006,64006],"mapped",[26292]],[[64007,64007],"mapped",[36667]],[[64008,64008],"mapped",[34892]],[[64009,64009],"mapped",[38477]],[[64010,64010],"mapped",[35211]],[[64011,64011],"mapped",[24275]],[[64012,64012],"mapped",[20800]],[[64013,64013],"mapped",[21952]],[[64014,64015],"valid"],[[64016,64016],"mapped",[22618]],[[64017,64017],"valid"],[[64018,64018],"mapped",[26228]],[[64019,64020],"valid"],[[64021,64021],"mapped",[20958]],[[64022,64022],"mapped",[29482]],[[64023,64023],"mapped",[30410]],[[64024,64024],"mapped",[31036]],[[64025,64025],"mapped",[31070]],[[64026,64026],"mapped",[31077]],[[64027,64027],"mapped",[31119]],[[64028,64028],"mapped",[38742]],[[64029,64029],"mapped",[31934]],[[64030,64030],"mapped",[32701]],[[64031,64031],"valid"],[[64032,64032],"mapped",[34322]],[[64033,64033],"valid"],[[64034,64034],"mapped",[35576]],[[64035,64036],"valid"],[[64037,64037],"mapped",[36920]],[[64038,64038],"mapped",[37117]],[[64039,64041],"valid"],[[64042,64042],"mapped",[39151]],[[64043,64043],"mapped",[39164]],[[64044,64044],"mapped",[39208]],[[64045,64045],"mapped",[40372]],[[64046,64046],"mapped",[37086]],[[64047,64047],"mapped",[38583]],[[64048,64048],"mapped",[20398]],[[64049,64049],"mapped",[20711]],[[64050,64050],"mapped",[20813]],[[64051,64051],"mapped",[21193]],[[64052,64052],"mapped",[21220]],[[64053,64053],"mapped",[21329]],[[64054,64054],"mapped",[21917]],[[64055,64055],"mapped",[22022]],[[64056,64056],"mapped",[22120]],[[64057,64057],"mapped",[22592]],[[64058,64058],"mapped",[22696]],[[64059,64059],"mapped",[23652]],[[64060,64060],"mapped",[23662]],[[64061,64061],"mapped",[24724]],[[64062,64062],"mapped",[24936]],[[64063,64063],"mapped",[24974]],[[64064,64064],"mapped",[25074]],[[64065,64065],"mapped",[25935]],[[64066,64066],"mapped",[26082]],[[64067,64067],"mapped",[26257]],[[64068,64068],"mapped",[26757]],[[64069,64069],"mapped",[28023]],[[64070,64070],"mapped",[28186]],[[64071,64071],"mapped",[28450]],[[64072,64072],"mapped",[29038]],[[64073,64073],"mapped",[29227]],[[64074,64074],"mapped",[29730]],[[64075,64075],"mapped",[30865]],[[64076,64076],"mapped",[31038]],[[64077,64077],"mapped",[31049]],[[64078,64078],"mapped",[31048]],[[64079,64079],"mapped",[31056]],[[64080,64080],"mapped",[31062]],[[64081,64081],"mapped",[31069]],[[64082,64082],"mapped",[31117]],[[64083,64083],"mapped",[31118]],[[64084,64084],"mapped",[31296]],[[64085,64085],"mapped",[31361]],[[64086,64086],"mapped",[31680]],[[64087,64087],"mapped",[32244]],[[64088,64088],"mapped",[32265]],[[64089,64089],"mapped",[32321]],[[64090,64090],"mapped",[32626]],[[64091,64091],"mapped",[32773]],[[64092,64092],"mapped",[33261]],[[64093,64094],"mapped",[33401]],[[64095,64095],"mapped",[33879]],[[64096,64096],"mapped",[35088]],[[64097,64097],"mapped",[35222]],[[64098,64098],"mapped",[35585]],[[64099,64099],"mapped",[35641]],[[64100,64100],"mapped",[36051]],[[64101,64101],"mapped",[36104]],[[64102,64102],"mapped",[36790]],[[64103,64103],"mapped",[36920]],[[64104,64104],"mapped",[38627]],[[64105,64105],"mapped",[38911]],[[64106,64106],"mapped",[38971]],[[64107,64107],"mapped",[24693]],[[64108,64108],"mapped",[148206]],[[64109,64109],"mapped",[33304]],[[64110,64111],"disallowed"],[[64112,64112],"mapped",[20006]],[[64113,64113],"mapped",[20917]],[[64114,64114],"mapped",[20840]],[[64115,64115],"mapped",[20352]],[[64116,64116],"mapped",[20805]],[[64117,64117],"mapped",[20864]],[[64118,64118],"mapped",[21191]],[[64119,64119],"mapped",[21242]],[[64120,64120],"mapped",[21917]],[[64121,64121],"mapped",[21845]],[[64122,64122],"mapped",[21913]],[[64123,64123],"mapped",[21986]],[[64124,64124],"mapped",[22618]],[[64125,64125],"mapped",[22707]],[[64126,64126],"mapped",[22852]],[[64127,64127],"mapped",[22868]],[[64128,64128],"mapped",[23138]],[[64129,64129],"mapped",[23336]],[[64130,64130],"mapped",[24274]],[[64131,64131],"mapped",[24281]],[[64132,64132],"mapped",[24425]],[[64133,64133],"mapped",[24493]],[[64134,64134],"mapped",[24792]],[[64135,64135],"mapped",[24910]],[[64136,64136],"mapped",[24840]],[[64137,64137],"mapped",[24974]],[[64138,64138],"mapped",[24928]],[[64139,64139],"mapped",[25074]],[[64140,64140],"mapped",[25140]],[[64141,64141],"mapped",[25540]],[[64142,64142],"mapped",[25628]],[[64143,64143],"mapped",[25682]],[[64144,64144],"mapped",[25942]],[[64145,64145],"mapped",[26228]],[[64146,64146],"mapped",[26391]],[[64147,64147],"mapped",[26395]],[[64148,64148],"mapped",[26454]],[[64149,64149],"mapped",[27513]],[[64150,64150],"mapped",[27578]],[[64151,64151],"mapped",[27969]],[[64152,64152],"mapped",[28379]],[[64153,64153],"mapped",[28363]],[[64154,64154],"mapped",[28450]],[[64155,64155],"mapped",[28702]],[[64156,64156],"mapped",[29038]],[[64157,64157],"mapped",[30631]],[[64158,64158],"mapped",[29237]],[[64159,64159],"mapped",[29359]],[[64160,64160],"mapped",[29482]],[[64161,64161],"mapped",[29809]],[[64162,64162],"mapped",[29958]],[[64163,64163],"mapped",[30011]],[[64164,64164],"mapped",[30237]],[[64165,64165],"mapped",[30239]],[[64166,64166],"mapped",[30410]],[[64167,64167],"mapped",[30427]],[[64168,64168],"mapped",[30452]],[[64169,64169],"mapped",[30538]],[[64170,64170],"mapped",[30528]],[[64171,64171],"mapped",[30924]],[[64172,64172],"mapped",[31409]],[[64173,64173],"mapped",[31680]],[[64174,64174],"mapped",[31867]],[[64175,64175],"mapped",[32091]],[[64176,64176],"mapped",[32244]],[[64177,64177],"mapped",[32574]],[[64178,64178],"mapped",[32773]],[[64179,64179],"mapped",[33618]],[[64180,64180],"mapped",[33775]],[[64181,64181],"mapped",[34681]],[[64182,64182],"mapped",[35137]],[[64183,64183],"mapped",[35206]],[[64184,64184],"mapped",[35222]],[[64185,64185],"mapped",[35519]],[[64186,64186],"mapped",[35576]],[[64187,64187],"mapped",[35531]],[[64188,64188],"mapped",[35585]],[[64189,64189],"mapped",[35582]],[[64190,64190],"mapped",[35565]],[[64191,64191],"mapped",[35641]],[[64192,64192],"mapped",[35722]],[[64193,64193],"mapped",[36104]],[[64194,64194],"mapped",[36664]],[[64195,64195],"mapped",[36978]],[[64196,64196],"mapped",[37273]],[[64197,64197],"mapped",[37494]],[[64198,64198],"mapped",[38524]],[[64199,64199],"mapped",[38627]],[[64200,64200],"mapped",[38742]],[[64201,64201],"mapped",[38875]],[[64202,64202],"mapped",[38911]],[[64203,64203],"mapped",[38923]],[[64204,64204],"mapped",[38971]],[[64205,64205],"mapped",[39698]],[[64206,64206],"mapped",[40860]],[[64207,64207],"mapped",[141386]],[[64208,64208],"mapped",[141380]],[[64209,64209],"mapped",[144341]],[[64210,64210],"mapped",[15261]],[[64211,64211],"mapped",[16408]],[[64212,64212],"mapped",[16441]],[[64213,64213],"mapped",[152137]],[[64214,64214],"mapped",[154832]],[[64215,64215],"mapped",[163539]],[[64216,64216],"mapped",[40771]],[[64217,64217],"mapped",[40846]],[[64218,64255],"disallowed"],[[64256,64256],"mapped",[102,102]],[[64257,64257],"mapped",[102,105]],[[64258,64258],"mapped",[102,108]],[[64259,64259],"mapped",[102,102,105]],[[64260,64260],"mapped",[102,102,108]],[[64261,64262],"mapped",[115,116]],[[64263,64274],"disallowed"],[[64275,64275],"mapped",[1396,1398]],[[64276,64276],"mapped",[1396,1381]],[[64277,64277],"mapped",[1396,1387]],[[64278,64278],"mapped",[1406,1398]],[[64279,64279],"mapped",[1396,1389]],[[64280,64284],"disallowed"],[[64285,64285],"mapped",[1497,1460]],[[64286,64286],"valid"],[[64287,64287],"mapped",[1522,1463]],[[64288,64288],"mapped",[1506]],[[64289,64289],"mapped",[1488]],[[64290,64290],"mapped",[1491]],[[64291,64291],"mapped",[1492]],[[64292,64292],"mapped",[1499]],[[64293,64293],"mapped",[1500]],[[64294,64294],"mapped",[1501]],[[64295,64295],"mapped",[1512]],[[64296,64296],"mapped",[1514]],[[64297,64297],"disallowed_STD3_mapped",[43]],[[64298,64298],"mapped",[1513,1473]],[[64299,64299],"mapped",[1513,1474]],[[64300,64300],"mapped",[1513,1468,1473]],[[64301,64301],"mapped",[1513,1468,1474]],[[64302,64302],"mapped",[1488,1463]],[[64303,64303],"mapped",[1488,1464]],[[64304,64304],"mapped",[1488,1468]],[[64305,64305],"mapped",[1489,1468]],[[64306,64306],"mapped",[1490,1468]],[[64307,64307],"mapped",[1491,1468]],[[64308,64308],"mapped",[1492,1468]],[[64309,64309],"mapped",[1493,1468]],[[64310,64310],"mapped",[1494,1468]],[[64311,64311],"disallowed"],[[64312,64312],"mapped",[1496,1468]],[[64313,64313],"mapped",[1497,1468]],[[64314,64314],"mapped",[1498,1468]],[[64315,64315],"mapped",[1499,1468]],[[64316,64316],"mapped",[1500,1468]],[[64317,64317],"disallowed"],[[64318,64318],"mapped",[1502,1468]],[[64319,64319],"disallowed"],[[64320,64320],"mapped",[1504,1468]],[[64321,64321],"mapped",[1505,1468]],[[64322,64322],"disallowed"],[[64323,64323],"mapped",[1507,1468]],[[64324,64324],"mapped",[1508,1468]],[[64325,64325],"disallowed"],[[64326,64326],"mapped",[1510,1468]],[[64327,64327],"mapped",[1511,1468]],[[64328,64328],"mapped",[1512,1468]],[[64329,64329],"mapped",[1513,1468]],[[64330,64330],"mapped",[1514,1468]],[[64331,64331],"mapped",[1493,1465]],[[64332,64332],"mapped",[1489,1471]],[[64333,64333],"mapped",[1499,1471]],[[64334,64334],"mapped",[1508,1471]],[[64335,64335],"mapped",[1488,1500]],[[64336,64337],"mapped",[1649]],[[64338,64341],"mapped",[1659]],[[64342,64345],"mapped",[1662]],[[64346,64349],"mapped",[1664]],[[64350,64353],"mapped",[1658]],[[64354,64357],"mapped",[1663]],[[64358,64361],"mapped",[1657]],[[64362,64365],"mapped",[1700]],[[64366,64369],"mapped",[1702]],[[64370,64373],"mapped",[1668]],[[64374,64377],"mapped",[1667]],[[64378,64381],"mapped",[1670]],[[64382,64385],"mapped",[1671]],[[64386,64387],"mapped",[1677]],[[64388,64389],"mapped",[1676]],[[64390,64391],"mapped",[1678]],[[64392,64393],"mapped",[1672]],[[64394,64395],"mapped",[1688]],[[64396,64397],"mapped",[1681]],[[64398,64401],"mapped",[1705]],[[64402,64405],"mapped",[1711]],[[64406,64409],"mapped",[1715]],[[64410,64413],"mapped",[1713]],[[64414,64415],"mapped",[1722]],[[64416,64419],"mapped",[1723]],[[64420,64421],"mapped",[1728]],[[64422,64425],"mapped",[1729]],[[64426,64429],"mapped",[1726]],[[64430,64431],"mapped",[1746]],[[64432,64433],"mapped",[1747]],[[64434,64449],"valid",[],"NV8"],[[64450,64466],"disallowed"],[[64467,64470],"mapped",[1709]],[[64471,64472],"mapped",[1735]],[[64473,64474],"mapped",[1734]],[[64475,64476],"mapped",[1736]],[[64477,64477],"mapped",[1735,1652]],[[64478,64479],"mapped",[1739]],[[64480,64481],"mapped",[1733]],[[64482,64483],"mapped",[1737]],[[64484,64487],"mapped",[1744]],[[64488,64489],"mapped",[1609]],[[64490,64491],"mapped",[1574,1575]],[[64492,64493],"mapped",[1574,1749]],[[64494,64495],"mapped",[1574,1608]],[[64496,64497],"mapped",[1574,1735]],[[64498,64499],"mapped",[1574,1734]],[[64500,64501],"mapped",[1574,1736]],[[64502,64504],"mapped",[1574,1744]],[[64505,64507],"mapped",[1574,1609]],[[64508,64511],"mapped",[1740]],[[64512,64512],"mapped",[1574,1580]],[[64513,64513],"mapped",[1574,1581]],[[64514,64514],"mapped",[1574,1605]],[[64515,64515],"mapped",[1574,1609]],[[64516,64516],"mapped",[1574,1610]],[[64517,64517],"mapped",[1576,1580]],[[64518,64518],"mapped",[1576,1581]],[[64519,64519],"mapped",[1576,1582]],[[64520,64520],"mapped",[1576,1605]],[[64521,64521],"mapped",[1576,1609]],[[64522,64522],"mapped",[1576,1610]],[[64523,64523],"mapped",[1578,1580]],[[64524,64524],"mapped",[1578,1581]],[[64525,64525],"mapped",[1578,1582]],[[64526,64526],"mapped",[1578,1605]],[[64527,64527],"mapped",[1578,1609]],[[64528,64528],"mapped",[1578,1610]],[[64529,64529],"mapped",[1579,1580]],[[64530,64530],"mapped",[1579,1605]],[[64531,64531],"mapped",[1579,1609]],[[64532,64532],"mapped",[1579,1610]],[[64533,64533],"mapped",[1580,1581]],[[64534,64534],"mapped",[1580,1605]],[[64535,64535],"mapped",[1581,1580]],[[64536,64536],"mapped",[1581,1605]],[[64537,64537],"mapped",[1582,1580]],[[64538,64538],"mapped",[1582,1581]],[[64539,64539],"mapped",[1582,1605]],[[64540,64540],"mapped",[1587,1580]],[[64541,64541],"mapped",[1587,1581]],[[64542,64542],"mapped",[1587,1582]],[[64543,64543],"mapped",[1587,1605]],[[64544,64544],"mapped",[1589,1581]],[[64545,64545],"mapped",[1589,1605]],[[64546,64546],"mapped",[1590,1580]],[[64547,64547],"mapped",[1590,1581]],[[64548,64548],"mapped",[1590,1582]],[[64549,64549],"mapped",[1590,1605]],[[64550,64550],"mapped",[1591,1581]],[[64551,64551],"mapped",[1591,1605]],[[64552,64552],"mapped",[1592,1605]],[[64553,64553],"mapped",[1593,1580]],[[64554,64554],"mapped",[1593,1605]],[[64555,64555],"mapped",[1594,1580]],[[64556,64556],"mapped",[1594,1605]],[[64557,64557],"mapped",[1601,1580]],[[64558,64558],"mapped",[1601,1581]],[[64559,64559],"mapped",[1601,1582]],[[64560,64560],"mapped",[1601,1605]],[[64561,64561],"mapped",[1601,1609]],[[64562,64562],"mapped",[1601,1610]],[[64563,64563],"mapped",[1602,1581]],[[64564,64564],"mapped",[1602,1605]],[[64565,64565],"mapped",[1602,1609]],[[64566,64566],"mapped",[1602,1610]],[[64567,64567],"mapped",[1603,1575]],[[64568,64568],"mapped",[1603,1580]],[[64569,64569],"mapped",[1603,1581]],[[64570,64570],"mapped",[1603,1582]],[[64571,64571],"mapped",[1603,1604]],[[64572,64572],"mapped",[1603,1605]],[[64573,64573],"mapped",[1603,1609]],[[64574,64574],"mapped",[1603,1610]],[[64575,64575],"mapped",[1604,1580]],[[64576,64576],"mapped",[1604,1581]],[[64577,64577],"mapped",[1604,1582]],[[64578,64578],"mapped",[1604,1605]],[[64579,64579],"mapped",[1604,1609]],[[64580,64580],"mapped",[1604,1610]],[[64581,64581],"mapped",[1605,1580]],[[64582,64582],"mapped",[1605,1581]],[[64583,64583],"mapped",[1605,1582]],[[64584,64584],"mapped",[1605,1605]],[[64585,64585],"mapped",[1605,1609]],[[64586,64586],"mapped",[1605,1610]],[[64587,64587],"mapped",[1606,1580]],[[64588,64588],"mapped",[1606,1581]],[[64589,64589],"mapped",[1606,1582]],[[64590,64590],"mapped",[1606,1605]],[[64591,64591],"mapped",[1606,1609]],[[64592,64592],"mapped",[1606,1610]],[[64593,64593],"mapped",[1607,1580]],[[64594,64594],"mapped",[1607,1605]],[[64595,64595],"mapped",[1607,1609]],[[64596,64596],"mapped",[1607,1610]],[[64597,64597],"mapped",[1610,1580]],[[64598,64598],"mapped",[1610,1581]],[[64599,64599],"mapped",[1610,1582]],[[64600,64600],"mapped",[1610,1605]],[[64601,64601],"mapped",[1610,1609]],[[64602,64602],"mapped",[1610,1610]],[[64603,64603],"mapped",[1584,1648]],[[64604,64604],"mapped",[1585,1648]],[[64605,64605],"mapped",[1609,1648]],[[64606,64606],"disallowed_STD3_mapped",[32,1612,1617]],[[64607,64607],"disallowed_STD3_mapped",[32,1613,1617]],[[64608,64608],"disallowed_STD3_mapped",[32,1614,1617]],[[64609,64609],"disallowed_STD3_mapped",[32,1615,1617]],[[64610,64610],"disallowed_STD3_mapped",[32,1616,1617]],[[64611,64611],"disallowed_STD3_mapped",[32,1617,1648]],[[64612,64612],"mapped",[1574,1585]],[[64613,64613],"mapped",[1574,1586]],[[64614,64614],"mapped",[1574,1605]],[[64615,64615],"mapped",[1574,1606]],[[64616,64616],"mapped",[1574,1609]],[[64617,64617],"mapped",[1574,1610]],[[64618,64618],"mapped",[1576,1585]],[[64619,64619],"mapped",[1576,1586]],[[64620,64620],"mapped",[1576,1605]],[[64621,64621],"mapped",[1576,1606]],[[64622,64622],"mapped",[1576,1609]],[[64623,64623],"mapped",[1576,1610]],[[64624,64624],"mapped",[1578,1585]],[[64625,64625],"mapped",[1578,1586]],[[64626,64626],"mapped",[1578,1605]],[[64627,64627],"mapped",[1578,1606]],[[64628,64628],"mapped",[1578,1609]],[[64629,64629],"mapped",[1578,1610]],[[64630,64630],"mapped",[1579,1585]],[[64631,64631],"mapped",[1579,1586]],[[64632,64632],"mapped",[1579,1605]],[[64633,64633],"mapped",[1579,1606]],[[64634,64634],"mapped",[1579,1609]],[[64635,64635],"mapped",[1579,1610]],[[64636,64636],"mapped",[1601,1609]],[[64637,64637],"mapped",[1601,1610]],[[64638,64638],"mapped",[1602,1609]],[[64639,64639],"mapped",[1602,1610]],[[64640,64640],"mapped",[1603,1575]],[[64641,64641],"mapped",[1603,1604]],[[64642,64642],"mapped",[1603,1605]],[[64643,64643],"mapped",[1603,1609]],[[64644,64644],"mapped",[1603,1610]],[[64645,64645],"mapped",[1604,1605]],[[64646,64646],"mapped",[1604,1609]],[[64647,64647],"mapped",[1604,1610]],[[64648,64648],"mapped",[1605,1575]],[[64649,64649],"mapped",[1605,1605]],[[64650,64650],"mapped",[1606,1585]],[[64651,64651],"mapped",[1606,1586]],[[64652,64652],"mapped",[1606,1605]],[[64653,64653],"mapped",[1606,1606]],[[64654,64654],"mapped",[1606,1609]],[[64655,64655],"mapped",[1606,1610]],[[64656,64656],"mapped",[1609,1648]],[[64657,64657],"mapped",[1610,1585]],[[64658,64658],"mapped",[1610,1586]],[[64659,64659],"mapped",[1610,1605]],[[64660,64660],"mapped",[1610,1606]],[[64661,64661],"mapped",[1610,1609]],[[64662,64662],"mapped",[1610,1610]],[[64663,64663],"mapped",[1574,1580]],[[64664,64664],"mapped",[1574,1581]],[[64665,64665],"mapped",[1574,1582]],[[64666,64666],"mapped",[1574,1605]],[[64667,64667],"mapped",[1574,1607]],[[64668,64668],"mapped",[1576,1580]],[[64669,64669],"mapped",[1576,1581]],[[64670,64670],"mapped",[1576,1582]],[[64671,64671],"mapped",[1576,1605]],[[64672,64672],"mapped",[1576,1607]],[[64673,64673],"mapped",[1578,1580]],[[64674,64674],"mapped",[1578,1581]],[[64675,64675],"mapped",[1578,1582]],[[64676,64676],"mapped",[1578,1605]],[[64677,64677],"mapped",[1578,1607]],[[64678,64678],"mapped",[1579,1605]],[[64679,64679],"mapped",[1580,1581]],[[64680,64680],"mapped",[1580,1605]],[[64681,64681],"mapped",[1581,1580]],[[64682,64682],"mapped",[1581,1605]],[[64683,64683],"mapped",[1582,1580]],[[64684,64684],"mapped",[1582,1605]],[[64685,64685],"mapped",[1587,1580]],[[64686,64686],"mapped",[1587,1581]],[[64687,64687],"mapped",[1587,1582]],[[64688,64688],"mapped",[1587,1605]],[[64689,64689],"mapped",[1589,1581]],[[64690,64690],"mapped",[1589,1582]],[[64691,64691],"mapped",[1589,1605]],[[64692,64692],"mapped",[1590,1580]],[[64693,64693],"mapped",[1590,1581]],[[64694,64694],"mapped",[1590,1582]],[[64695,64695],"mapped",[1590,1605]],[[64696,64696],"mapped",[1591,1581]],[[64697,64697],"mapped",[1592,1605]],[[64698,64698],"mapped",[1593,1580]],[[64699,64699],"mapped",[1593,1605]],[[64700,64700],"mapped",[1594,1580]],[[64701,64701],"mapped",[1594,1605]],[[64702,64702],"mapped",[1601,1580]],[[64703,64703],"mapped",[1601,1581]],[[64704,64704],"mapped",[1601,1582]],[[64705,64705],"mapped",[1601,1605]],[[64706,64706],"mapped",[1602,1581]],[[64707,64707],"mapped",[1602,1605]],[[64708,64708],"mapped",[1603,1580]],[[64709,64709],"mapped",[1603,1581]],[[64710,64710],"mapped",[1603,1582]],[[64711,64711],"mapped",[1603,1604]],[[64712,64712],"mapped",[1603,1605]],[[64713,64713],"mapped",[1604,1580]],[[64714,64714],"mapped",[1604,1581]],[[64715,64715],"mapped",[1604,1582]],[[64716,64716],"mapped",[1604,1605]],[[64717,64717],"mapped",[1604,1607]],[[64718,64718],"mapped",[1605,1580]],[[64719,64719],"mapped",[1605,1581]],[[64720,64720],"mapped",[1605,1582]],[[64721,64721],"mapped",[1605,1605]],[[64722,64722],"mapped",[1606,1580]],[[64723,64723],"mapped",[1606,1581]],[[64724,64724],"mapped",[1606,1582]],[[64725,64725],"mapped",[1606,1605]],[[64726,64726],"mapped",[1606,1607]],[[64727,64727],"mapped",[1607,1580]],[[64728,64728],"mapped",[1607,1605]],[[64729,64729],"mapped",[1607,1648]],[[64730,64730],"mapped",[1610,1580]],[[64731,64731],"mapped",[1610,1581]],[[64732,64732],"mapped",[1610,1582]],[[64733,64733],"mapped",[1610,1605]],[[64734,64734],"mapped",[1610,1607]],[[64735,64735],"mapped",[1574,1605]],[[64736,64736],"mapped",[1574,1607]],[[64737,64737],"mapped",[1576,1605]],[[64738,64738],"mapped",[1576,1607]],[[64739,64739],"mapped",[1578,1605]],[[64740,64740],"mapped",[1578,1607]],[[64741,64741],"mapped",[1579,1605]],[[64742,64742],"mapped",[1579,1607]],[[64743,64743],"mapped",[1587,1605]],[[64744,64744],"mapped",[1587,1607]],[[64745,64745],"mapped",[1588,1605]],[[64746,64746],"mapped",[1588,1607]],[[64747,64747],"mapped",[1603,1604]],[[64748,64748],"mapped",[1603,1605]],[[64749,64749],"mapped",[1604,1605]],[[64750,64750],"mapped",[1606,1605]],[[64751,64751],"mapped",[1606,1607]],[[64752,64752],"mapped",[1610,1605]],[[64753,64753],"mapped",[1610,1607]],[[64754,64754],"mapped",[1600,1614,1617]],[[64755,64755],"mapped",[1600,1615,1617]],[[64756,64756],"mapped",[1600,1616,1617]],[[64757,64757],"mapped",[1591,1609]],[[64758,64758],"mapped",[1591,1610]],[[64759,64759],"mapped",[1593,1609]],[[64760,64760],"mapped",[1593,1610]],[[64761,64761],"mapped",[1594,1609]],[[64762,64762],"mapped",[1594,1610]],[[64763,64763],"mapped",[1587,1609]],[[64764,64764],"mapped",[1587,1610]],[[64765,64765],"mapped",[1588,1609]],[[64766,64766],"mapped",[1588,1610]],[[64767,64767],"mapped",[1581,1609]],[[64768,64768],"mapped",[1581,1610]],[[64769,64769],"mapped",[1580,1609]],[[64770,64770],"mapped",[1580,1610]],[[64771,64771],"mapped",[1582,1609]],[[64772,64772],"mapped",[1582,1610]],[[64773,64773],"mapped",[1589,1609]],[[64774,64774],"mapped",[1589,1610]],[[64775,64775],"mapped",[1590,1609]],[[64776,64776],"mapped",[1590,1610]],[[64777,64777],"mapped",[1588,1580]],[[64778,64778],"mapped",[1588,1581]],[[64779,64779],"mapped",[1588,1582]],[[64780,64780],"mapped",[1588,1605]],[[64781,64781],"mapped",[1588,1585]],[[64782,64782],"mapped",[1587,1585]],[[64783,64783],"mapped",[1589,1585]],[[64784,64784],"mapped",[1590,1585]],[[64785,64785],"mapped",[1591,1609]],[[64786,64786],"mapped",[1591,1610]],[[64787,64787],"mapped",[1593,1609]],[[64788,64788],"mapped",[1593,1610]],[[64789,64789],"mapped",[1594,1609]],[[64790,64790],"mapped",[1594,1610]],[[64791,64791],"mapped",[1587,1609]],[[64792,64792],"mapped",[1587,1610]],[[64793,64793],"mapped",[1588,1609]],[[64794,64794],"mapped",[1588,1610]],[[64795,64795],"mapped",[1581,1609]],[[64796,64796],"mapped",[1581,1610]],[[64797,64797],"mapped",[1580,1609]],[[64798,64798],"mapped",[1580,1610]],[[64799,64799],"mapped",[1582,1609]],[[64800,64800],"mapped",[1582,1610]],[[64801,64801],"mapped",[1589,1609]],[[64802,64802],"mapped",[1589,1610]],[[64803,64803],"mapped",[1590,1609]],[[64804,64804],"mapped",[1590,1610]],[[64805,64805],"mapped",[1588,1580]],[[64806,64806],"mapped",[1588,1581]],[[64807,64807],"mapped",[1588,1582]],[[64808,64808],"mapped",[1588,1605]],[[64809,64809],"mapped",[1588,1585]],[[64810,64810],"mapped",[1587,1585]],[[64811,64811],"mapped",[1589,1585]],[[64812,64812],"mapped",[1590,1585]],[[64813,64813],"mapped",[1588,1580]],[[64814,64814],"mapped",[1588,1581]],[[64815,64815],"mapped",[1588,1582]],[[64816,64816],"mapped",[1588,1605]],[[64817,64817],"mapped",[1587,1607]],[[64818,64818],"mapped",[1588,1607]],[[64819,64819],"mapped",[1591,1605]],[[64820,64820],"mapped",[1587,1580]],[[64821,64821],"mapped",[1587,1581]],[[64822,64822],"mapped",[1587,1582]],[[64823,64823],"mapped",[1588,1580]],[[64824,64824],"mapped",[1588,1581]],[[64825,64825],"mapped",[1588,1582]],[[64826,64826],"mapped",[1591,1605]],[[64827,64827],"mapped",[1592,1605]],[[64828,64829],"mapped",[1575,1611]],[[64830,64831],"valid",[],"NV8"],[[64832,64847],"disallowed"],[[64848,64848],"mapped",[1578,1580,1605]],[[64849,64850],"mapped",[1578,1581,1580]],[[64851,64851],"mapped",[1578,1581,1605]],[[64852,64852],"mapped",[1578,1582,1605]],[[64853,64853],"mapped",[1578,1605,1580]],[[64854,64854],"mapped",[1578,1605,1581]],[[64855,64855],"mapped",[1578,1605,1582]],[[64856,64857],"mapped",[1580,1605,1581]],[[64858,64858],"mapped",[1581,1605,1610]],[[64859,64859],"mapped",[1581,1605,1609]],[[64860,64860],"mapped",[1587,1581,1580]],[[64861,64861],"mapped",[1587,1580,1581]],[[64862,64862],"mapped",[1587,1580,1609]],[[64863,64864],"mapped",[1587,1605,1581]],[[64865,64865],"mapped",[1587,1605,1580]],[[64866,64867],"mapped",[1587,1605,1605]],[[64868,64869],"mapped",[1589,1581,1581]],[[64870,64870],"mapped",[1589,1605,1605]],[[64871,64872],"mapped",[1588,1581,1605]],[[64873,64873],"mapped",[1588,1580,1610]],[[64874,64875],"mapped",[1588,1605,1582]],[[64876,64877],"mapped",[1588,1605,1605]],[[64878,64878],"mapped",[1590,1581,1609]],[[64879,64880],"mapped",[1590,1582,1605]],[[64881,64882],"mapped",[1591,1605,1581]],[[64883,64883],"mapped",[1591,1605,1605]],[[64884,64884],"mapped",[1591,1605,1610]],[[64885,64885],"mapped",[1593,1580,1605]],[[64886,64887],"mapped",[1593,1605,1605]],[[64888,64888],"mapped",[1593,1605,1609]],[[64889,64889],"mapped",[1594,1605,1605]],[[64890,64890],"mapped",[1594,1605,1610]],[[64891,64891],"mapped",[1594,1605,1609]],[[64892,64893],"mapped",[1601,1582,1605]],[[64894,64894],"mapped",[1602,1605,1581]],[[64895,64895],"mapped",[1602,1605,1605]],[[64896,64896],"mapped",[1604,1581,1605]],[[64897,64897],"mapped",[1604,1581,1610]],[[64898,64898],"mapped",[1604,1581,1609]],[[64899,64900],"mapped",[1604,1580,1580]],[[64901,64902],"mapped",[1604,1582,1605]],[[64903,64904],"mapped",[1604,1605,1581]],[[64905,64905],"mapped",[1605,1581,1580]],[[64906,64906],"mapped",[1605,1581,1605]],[[64907,64907],"mapped",[1605,1581,1610]],[[64908,64908],"mapped",[1605,1580,1581]],[[64909,64909],"mapped",[1605,1580,1605]],[[64910,64910],"mapped",[1605,1582,1580]],[[64911,64911],"mapped",[1605,1582,1605]],[[64912,64913],"disallowed"],[[64914,64914],"mapped",[1605,1580,1582]],[[64915,64915],"mapped",[1607,1605,1580]],[[64916,64916],"mapped",[1607,1605,1605]],[[64917,64917],"mapped",[1606,1581,1605]],[[64918,64918],"mapped",[1606,1581,1609]],[[64919,64920],"mapped",[1606,1580,1605]],[[64921,64921],"mapped",[1606,1580,1609]],[[64922,64922],"mapped",[1606,1605,1610]],[[64923,64923],"mapped",[1606,1605,1609]],[[64924,64925],"mapped",[1610,1605,1605]],[[64926,64926],"mapped",[1576,1582,1610]],[[64927,64927],"mapped",[1578,1580,1610]],[[64928,64928],"mapped",[1578,1580,1609]],[[64929,64929],"mapped",[1578,1582,1610]],[[64930,64930],"mapped",[1578,1582,1609]],[[64931,64931],"mapped",[1578,1605,1610]],[[64932,64932],"mapped",[1578,1605,1609]],[[64933,64933],"mapped",[1580,1605,1610]],[[64934,64934],"mapped",[1580,1581,1609]],[[64935,64935],"mapped",[1580,1605,1609]],[[64936,64936],"mapped",[1587,1582,1609]],[[64937,64937],"mapped",[1589,1581,1610]],[[64938,64938],"mapped",[1588,1581,1610]],[[64939,64939],"mapped",[1590,1581,1610]],[[64940,64940],"mapped",[1604,1580,1610]],[[64941,64941],"mapped",[1604,1605,1610]],[[64942,64942],"mapped",[1610,1581,1610]],[[64943,64943],"mapped",[1610,1580,1610]],[[64944,64944],"mapped",[1610,1605,1610]],[[64945,64945],"mapped",[1605,1605,1610]],[[64946,64946],"mapped",[1602,1605,1610]],[[64947,64947],"mapped",[1606,1581,1610]],[[64948,64948],"mapped",[1602,1605,1581]],[[64949,64949],"mapped",[1604,1581,1605]],[[64950,64950],"mapped",[1593,1605,1610]],[[64951,64951],"mapped",[1603,1605,1610]],[[64952,64952],"mapped",[1606,1580,1581]],[[64953,64953],"mapped",[1605,1582,1610]],[[64954,64954],"mapped",[1604,1580,1605]],[[64955,64955],"mapped",[1603,1605,1605]],[[64956,64956],"mapped",[1604,1580,1605]],[[64957,64957],"mapped",[1606,1580,1581]],[[64958,64958],"mapped",[1580,1581,1610]],[[64959,64959],"mapped",[1581,1580,1610]],[[64960,64960],"mapped",[1605,1580,1610]],[[64961,64961],"mapped",[1601,1605,1610]],[[64962,64962],"mapped",[1576,1581,1610]],[[64963,64963],"mapped",[1603,1605,1605]],[[64964,64964],"mapped",[1593,1580,1605]],[[64965,64965],"mapped",[1589,1605,1605]],[[64966,64966],"mapped",[1587,1582,1610]],[[64967,64967],"mapped",[1606,1580,1610]],[[64968,64975],"disallowed"],[[64976,65007],"disallowed"],[[65008,65008],"mapped",[1589,1604,1746]],[[65009,65009],"mapped",[1602,1604,1746]],[[65010,65010],"mapped",[1575,1604,1604,1607]],[[65011,65011],"mapped",[1575,1603,1576,1585]],[[65012,65012],"mapped",[1605,1581,1605,1583]],[[65013,65013],"mapped",[1589,1604,1593,1605]],[[65014,65014],"mapped",[1585,1587,1608,1604]],[[65015,65015],"mapped",[1593,1604,1610,1607]],[[65016,65016],"mapped",[1608,1587,1604,1605]],[[65017,65017],"mapped",[1589,1604,1609]],[[65018,65018],"disallowed_STD3_mapped",[1589,1604,1609,32,1575,1604,1604,1607,32,1593,1604,1610,1607,32,1608,1587,1604,1605]],[[65019,65019],"disallowed_STD3_mapped",[1580,1604,32,1580,1604,1575,1604,1607]],[[65020,65020],"mapped",[1585,1740,1575,1604]],[[65021,65021],"valid",[],"NV8"],[[65022,65023],"disallowed"],[[65024,65039],"ignored"],[[65040,65040],"disallowed_STD3_mapped",[44]],[[65041,65041],"mapped",[12289]],[[65042,65042],"disallowed"],[[65043,65043],"disallowed_STD3_mapped",[58]],[[65044,65044],"disallowed_STD3_mapped",[59]],[[65045,65045],"disallowed_STD3_mapped",[33]],[[65046,65046],"disallowed_STD3_mapped",[63]],[[65047,65047],"mapped",[12310]],[[65048,65048],"mapped",[12311]],[[65049,65049],"disallowed"],[[65050,65055],"disallowed"],[[65056,65059],"valid"],[[65060,65062],"valid"],[[65063,65069],"valid"],[[65070,65071],"valid"],[[65072,65072],"disallowed"],[[65073,65073],"mapped",[8212]],[[65074,65074],"mapped",[8211]],[[65075,65076],"disallowed_STD3_mapped",[95]],[[65077,65077],"disallowed_STD3_mapped",[40]],[[65078,65078],"disallowed_STD3_mapped",[41]],[[65079,65079],"disallowed_STD3_mapped",[123]],[[65080,65080],"disallowed_STD3_mapped",[125]],[[65081,65081],"mapped",[12308]],[[65082,65082],"mapped",[12309]],[[65083,65083],"mapped",[12304]],[[65084,65084],"mapped",[12305]],[[65085,65085],"mapped",[12298]],[[65086,65086],"mapped",[12299]],[[65087,65087],"mapped",[12296]],[[65088,65088],"mapped",[12297]],[[65089,65089],"mapped",[12300]],[[65090,65090],"mapped",[12301]],[[65091,65091],"mapped",[12302]],[[65092,65092],"mapped",[12303]],[[65093,65094],"valid",[],"NV8"],[[65095,65095],"disallowed_STD3_mapped",[91]],[[65096,65096],"disallowed_STD3_mapped",[93]],[[65097,65100],"disallowed_STD3_mapped",[32,773]],[[65101,65103],"disallowed_STD3_mapped",[95]],[[65104,65104],"disallowed_STD3_mapped",[44]],[[65105,65105],"mapped",[12289]],[[65106,65106],"disallowed"],[[65107,65107],"disallowed"],[[65108,65108],"disallowed_STD3_mapped",[59]],[[65109,65109],"disallowed_STD3_mapped",[58]],[[65110,65110],"disallowed_STD3_mapped",[63]],[[65111,65111],"disallowed_STD3_mapped",[33]],[[65112,65112],"mapped",[8212]],[[65113,65113],"disallowed_STD3_mapped",[40]],[[65114,65114],"disallowed_STD3_mapped",[41]],[[65115,65115],"disallowed_STD3_mapped",[123]],[[65116,65116],"disallowed_STD3_mapped",[125]],[[65117,65117],"mapped",[12308]],[[65118,65118],"mapped",[12309]],[[65119,65119],"disallowed_STD3_mapped",[35]],[[65120,65120],"disallowed_STD3_mapped",[38]],[[65121,65121],"disallowed_STD3_mapped",[42]],[[65122,65122],"disallowed_STD3_mapped",[43]],[[65123,65123],"mapped",[45]],[[65124,65124],"disallowed_STD3_mapped",[60]],[[65125,65125],"disallowed_STD3_mapped",[62]],[[65126,65126],"disallowed_STD3_mapped",[61]],[[65127,65127],"disallowed"],[[65128,65128],"disallowed_STD3_mapped",[92]],[[65129,65129],"disallowed_STD3_mapped",[36]],[[65130,65130],"disallowed_STD3_mapped",[37]],[[65131,65131],"disallowed_STD3_mapped",[64]],[[65132,65135],"disallowed"],[[65136,65136],"disallowed_STD3_mapped",[32,1611]],[[65137,65137],"mapped",[1600,1611]],[[65138,65138],"disallowed_STD3_mapped",[32,1612]],[[65139,65139],"valid"],[[65140,65140],"disallowed_STD3_mapped",[32,1613]],[[65141,65141],"disallowed"],[[65142,65142],"disallowed_STD3_mapped",[32,1614]],[[65143,65143],"mapped",[1600,1614]],[[65144,65144],"disallowed_STD3_mapped",[32,1615]],[[65145,65145],"mapped",[1600,1615]],[[65146,65146],"disallowed_STD3_mapped",[32,1616]],[[65147,65147],"mapped",[1600,1616]],[[65148,65148],"disallowed_STD3_mapped",[32,1617]],[[65149,65149],"mapped",[1600,1617]],[[65150,65150],"disallowed_STD3_mapped",[32,1618]],[[65151,65151],"mapped",[1600,1618]],[[65152,65152],"mapped",[1569]],[[65153,65154],"mapped",[1570]],[[65155,65156],"mapped",[1571]],[[65157,65158],"mapped",[1572]],[[65159,65160],"mapped",[1573]],[[65161,65164],"mapped",[1574]],[[65165,65166],"mapped",[1575]],[[65167,65170],"mapped",[1576]],[[65171,65172],"mapped",[1577]],[[65173,65176],"mapped",[1578]],[[65177,65180],"mapped",[1579]],[[65181,65184],"mapped",[1580]],[[65185,65188],"mapped",[1581]],[[65189,65192],"mapped",[1582]],[[65193,65194],"mapped",[1583]],[[65195,65196],"mapped",[1584]],[[65197,65198],"mapped",[1585]],[[65199,65200],"mapped",[1586]],[[65201,65204],"mapped",[1587]],[[65205,65208],"mapped",[1588]],[[65209,65212],"mapped",[1589]],[[65213,65216],"mapped",[1590]],[[65217,65220],"mapped",[1591]],[[65221,65224],"mapped",[1592]],[[65225,65228],"mapped",[1593]],[[65229,65232],"mapped",[1594]],[[65233,65236],"mapped",[1601]],[[65237,65240],"mapped",[1602]],[[65241,65244],"mapped",[1603]],[[65245,65248],"mapped",[1604]],[[65249,65252],"mapped",[1605]],[[65253,65256],"mapped",[1606]],[[65257,65260],"mapped",[1607]],[[65261,65262],"mapped",[1608]],[[65263,65264],"mapped",[1609]],[[65265,65268],"mapped",[1610]],[[65269,65270],"mapped",[1604,1570]],[[65271,65272],"mapped",[1604,1571]],[[65273,65274],"mapped",[1604,1573]],[[65275,65276],"mapped",[1604,1575]],[[65277,65278],"disallowed"],[[65279,65279],"ignored"],[[65280,65280],"disallowed"],[[65281,65281],"disallowed_STD3_mapped",[33]],[[65282,65282],"disallowed_STD3_mapped",[34]],[[65283,65283],"disallowed_STD3_mapped",[35]],[[65284,65284],"disallowed_STD3_mapped",[36]],[[65285,65285],"disallowed_STD3_mapped",[37]],[[65286,65286],"disallowed_STD3_mapped",[38]],[[65287,65287],"disallowed_STD3_mapped",[39]],[[65288,65288],"disallowed_STD3_mapped",[40]],[[65289,65289],"disallowed_STD3_mapped",[41]],[[65290,65290],"disallowed_STD3_mapped",[42]],[[65291,65291],"disallowed_STD3_mapped",[43]],[[65292,65292],"disallowed_STD3_mapped",[44]],[[65293,65293],"mapped",[45]],[[65294,65294],"mapped",[46]],[[65295,65295],"disallowed_STD3_mapped",[47]],[[65296,65296],"mapped",[48]],[[65297,65297],"mapped",[49]],[[65298,65298],"mapped",[50]],[[65299,65299],"mapped",[51]],[[65300,65300],"mapped",[52]],[[65301,65301],"mapped",[53]],[[65302,65302],"mapped",[54]],[[65303,65303],"mapped",[55]],[[65304,65304],"mapped",[56]],[[65305,65305],"mapped",[57]],[[65306,65306],"disallowed_STD3_mapped",[58]],[[65307,65307],"disallowed_STD3_mapped",[59]],[[65308,65308],"disallowed_STD3_mapped",[60]],[[65309,65309],"disallowed_STD3_mapped",[61]],[[65310,65310],"disallowed_STD3_mapped",[62]],[[65311,65311],"disallowed_STD3_mapped",[63]],[[65312,65312],"disallowed_STD3_mapped",[64]],[[65313,65313],"mapped",[97]],[[65314,65314],"mapped",[98]],[[65315,65315],"mapped",[99]],[[65316,65316],"mapped",[100]],[[65317,65317],"mapped",[101]],[[65318,65318],"mapped",[102]],[[65319,65319],"mapped",[103]],[[65320,65320],"mapped",[104]],[[65321,65321],"mapped",[105]],[[65322,65322],"mapped",[106]],[[65323,65323],"mapped",[107]],[[65324,65324],"mapped",[108]],[[65325,65325],"mapped",[109]],[[65326,65326],"mapped",[110]],[[65327,65327],"mapped",[111]],[[65328,65328],"mapped",[112]],[[65329,65329],"mapped",[113]],[[65330,65330],"mapped",[114]],[[65331,65331],"mapped",[115]],[[65332,65332],"mapped",[116]],[[65333,65333],"mapped",[117]],[[65334,65334],"mapped",[118]],[[65335,65335],"mapped",[119]],[[65336,65336],"mapped",[120]],[[65337,65337],"mapped",[121]],[[65338,65338],"mapped",[122]],[[65339,65339],"disallowed_STD3_mapped",[91]],[[65340,65340],"disallowed_STD3_mapped",[92]],[[65341,65341],"disallowed_STD3_mapped",[93]],[[65342,65342],"disallowed_STD3_mapped",[94]],[[65343,65343],"disallowed_STD3_mapped",[95]],[[65344,65344],"disallowed_STD3_mapped",[96]],[[65345,65345],"mapped",[97]],[[65346,65346],"mapped",[98]],[[65347,65347],"mapped",[99]],[[65348,65348],"mapped",[100]],[[65349,65349],"mapped",[101]],[[65350,65350],"mapped",[102]],[[65351,65351],"mapped",[103]],[[65352,65352],"mapped",[104]],[[65353,65353],"mapped",[105]],[[65354,65354],"mapped",[106]],[[65355,65355],"mapped",[107]],[[65356,65356],"mapped",[108]],[[65357,65357],"mapped",[109]],[[65358,65358],"mapped",[110]],[[65359,65359],"mapped",[111]],[[65360,65360],"mapped",[112]],[[65361,65361],"mapped",[113]],[[65362,65362],"mapped",[114]],[[65363,65363],"mapped",[115]],[[65364,65364],"mapped",[116]],[[65365,65365],"mapped",[117]],[[65366,65366],"mapped",[118]],[[65367,65367],"mapped",[119]],[[65368,65368],"mapped",[120]],[[65369,65369],"mapped",[121]],[[65370,65370],"mapped",[122]],[[65371,65371],"disallowed_STD3_mapped",[123]],[[65372,65372],"disallowed_STD3_mapped",[124]],[[65373,65373],"disallowed_STD3_mapped",[125]],[[65374,65374],"disallowed_STD3_mapped",[126]],[[65375,65375],"mapped",[10629]],[[65376,65376],"mapped",[10630]],[[65377,65377],"mapped",[46]],[[65378,65378],"mapped",[12300]],[[65379,65379],"mapped",[12301]],[[65380,65380],"mapped",[12289]],[[65381,65381],"mapped",[12539]],[[65382,65382],"mapped",[12530]],[[65383,65383],"mapped",[12449]],[[65384,65384],"mapped",[12451]],[[65385,65385],"mapped",[12453]],[[65386,65386],"mapped",[12455]],[[65387,65387],"mapped",[12457]],[[65388,65388],"mapped",[12515]],[[65389,65389],"mapped",[12517]],[[65390,65390],"mapped",[12519]],[[65391,65391],"mapped",[12483]],[[65392,65392],"mapped",[12540]],[[65393,65393],"mapped",[12450]],[[65394,65394],"mapped",[12452]],[[65395,65395],"mapped",[12454]],[[65396,65396],"mapped",[12456]],[[65397,65397],"mapped",[12458]],[[65398,65398],"mapped",[12459]],[[65399,65399],"mapped",[12461]],[[65400,65400],"mapped",[12463]],[[65401,65401],"mapped",[12465]],[[65402,65402],"mapped",[12467]],[[65403,65403],"mapped",[12469]],[[65404,65404],"mapped",[12471]],[[65405,65405],"mapped",[12473]],[[65406,65406],"mapped",[12475]],[[65407,65407],"mapped",[12477]],[[65408,65408],"mapped",[12479]],[[65409,65409],"mapped",[12481]],[[65410,65410],"mapped",[12484]],[[65411,65411],"mapped",[12486]],[[65412,65412],"mapped",[12488]],[[65413,65413],"mapped",[12490]],[[65414,65414],"mapped",[12491]],[[65415,65415],"mapped",[12492]],[[65416,65416],"mapped",[12493]],[[65417,65417],"mapped",[12494]],[[65418,65418],"mapped",[12495]],[[65419,65419],"mapped",[12498]],[[65420,65420],"mapped",[12501]],[[65421,65421],"mapped",[12504]],[[65422,65422],"mapped",[12507]],[[65423,65423],"mapped",[12510]],[[65424,65424],"mapped",[12511]],[[65425,65425],"mapped",[12512]],[[65426,65426],"mapped",[12513]],[[65427,65427],"mapped",[12514]],[[65428,65428],"mapped",[12516]],[[65429,65429],"mapped",[12518]],[[65430,65430],"mapped",[12520]],[[65431,65431],"mapped",[12521]],[[65432,65432],"mapped",[12522]],[[65433,65433],"mapped",[12523]],[[65434,65434],"mapped",[12524]],[[65435,65435],"mapped",[12525]],[[65436,65436],"mapped",[12527]],[[65437,65437],"mapped",[12531]],[[65438,65438],"mapped",[12441]],[[65439,65439],"mapped",[12442]],[[65440,65440],"disallowed"],[[65441,65441],"mapped",[4352]],[[65442,65442],"mapped",[4353]],[[65443,65443],"mapped",[4522]],[[65444,65444],"mapped",[4354]],[[65445,65445],"mapped",[4524]],[[65446,65446],"mapped",[4525]],[[65447,65447],"mapped",[4355]],[[65448,65448],"mapped",[4356]],[[65449,65449],"mapped",[4357]],[[65450,65450],"mapped",[4528]],[[65451,65451],"mapped",[4529]],[[65452,65452],"mapped",[4530]],[[65453,65453],"mapped",[4531]],[[65454,65454],"mapped",[4532]],[[65455,65455],"mapped",[4533]],[[65456,65456],"mapped",[4378]],[[65457,65457],"mapped",[4358]],[[65458,65458],"mapped",[4359]],[[65459,65459],"mapped",[4360]],[[65460,65460],"mapped",[4385]],[[65461,65461],"mapped",[4361]],[[65462,65462],"mapped",[4362]],[[65463,65463],"mapped",[4363]],[[65464,65464],"mapped",[4364]],[[65465,65465],"mapped",[4365]],[[65466,65466],"mapped",[4366]],[[65467,65467],"mapped",[4367]],[[65468,65468],"mapped",[4368]],[[65469,65469],"mapped",[4369]],[[65470,65470],"mapped",[4370]],[[65471,65473],"disallowed"],[[65474,65474],"mapped",[4449]],[[65475,65475],"mapped",[4450]],[[65476,65476],"mapped",[4451]],[[65477,65477],"mapped",[4452]],[[65478,65478],"mapped",[4453]],[[65479,65479],"mapped",[4454]],[[65480,65481],"disallowed"],[[65482,65482],"mapped",[4455]],[[65483,65483],"mapped",[4456]],[[65484,65484],"mapped",[4457]],[[65485,65485],"mapped",[4458]],[[65486,65486],"mapped",[4459]],[[65487,65487],"mapped",[4460]],[[65488,65489],"disallowed"],[[65490,65490],"mapped",[4461]],[[65491,65491],"mapped",[4462]],[[65492,65492],"mapped",[4463]],[[65493,65493],"mapped",[4464]],[[65494,65494],"mapped",[4465]],[[65495,65495],"mapped",[4466]],[[65496,65497],"disallowed"],[[65498,65498],"mapped",[4467]],[[65499,65499],"mapped",[4468]],[[65500,65500],"mapped",[4469]],[[65501,65503],"disallowed"],[[65504,65504],"mapped",[162]],[[65505,65505],"mapped",[163]],[[65506,65506],"mapped",[172]],[[65507,65507],"disallowed_STD3_mapped",[32,772]],[[65508,65508],"mapped",[166]],[[65509,65509],"mapped",[165]],[[65510,65510],"mapped",[8361]],[[65511,65511],"disallowed"],[[65512,65512],"mapped",[9474]],[[65513,65513],"mapped",[8592]],[[65514,65514],"mapped",[8593]],[[65515,65515],"mapped",[8594]],[[65516,65516],"mapped",[8595]],[[65517,65517],"mapped",[9632]],[[65518,65518],"mapped",[9675]],[[65519,65528],"disallowed"],[[65529,65531],"disallowed"],[[65532,65532],"disallowed"],[[65533,65533],"disallowed"],[[65534,65535],"disallowed"],[[65536,65547],"valid"],[[65548,65548],"disallowed"],[[65549,65574],"valid"],[[65575,65575],"disallowed"],[[65576,65594],"valid"],[[65595,65595],"disallowed"],[[65596,65597],"valid"],[[65598,65598],"disallowed"],[[65599,65613],"valid"],[[65614,65615],"disallowed"],[[65616,65629],"valid"],[[65630,65663],"disallowed"],[[65664,65786],"valid"],[[65787,65791],"disallowed"],[[65792,65794],"valid",[],"NV8"],[[65795,65798],"disallowed"],[[65799,65843],"valid",[],"NV8"],[[65844,65846],"disallowed"],[[65847,65855],"valid",[],"NV8"],[[65856,65930],"valid",[],"NV8"],[[65931,65932],"valid",[],"NV8"],[[65933,65935],"disallowed"],[[65936,65947],"valid",[],"NV8"],[[65948,65951],"disallowed"],[[65952,65952],"valid",[],"NV8"],[[65953,65999],"disallowed"],[[66000,66044],"valid",[],"NV8"],[[66045,66045],"valid"],[[66046,66175],"disallowed"],[[66176,66204],"valid"],[[66205,66207],"disallowed"],[[66208,66256],"valid"],[[66257,66271],"disallowed"],[[66272,66272],"valid"],[[66273,66299],"valid",[],"NV8"],[[66300,66303],"disallowed"],[[66304,66334],"valid"],[[66335,66335],"valid"],[[66336,66339],"valid",[],"NV8"],[[66340,66351],"disallowed"],[[66352,66368],"valid"],[[66369,66369],"valid",[],"NV8"],[[66370,66377],"valid"],[[66378,66378],"valid",[],"NV8"],[[66379,66383],"disallowed"],[[66384,66426],"valid"],[[66427,66431],"disallowed"],[[66432,66461],"valid"],[[66462,66462],"disallowed"],[[66463,66463],"valid",[],"NV8"],[[66464,66499],"valid"],[[66500,66503],"disallowed"],[[66504,66511],"valid"],[[66512,66517],"valid",[],"NV8"],[[66518,66559],"disallowed"],[[66560,66560],"mapped",[66600]],[[66561,66561],"mapped",[66601]],[[66562,66562],"mapped",[66602]],[[66563,66563],"mapped",[66603]],[[66564,66564],"mapped",[66604]],[[66565,66565],"mapped",[66605]],[[66566,66566],"mapped",[66606]],[[66567,66567],"mapped",[66607]],[[66568,66568],"mapped",[66608]],[[66569,66569],"mapped",[66609]],[[66570,66570],"mapped",[66610]],[[66571,66571],"mapped",[66611]],[[66572,66572],"mapped",[66612]],[[66573,66573],"mapped",[66613]],[[66574,66574],"mapped",[66614]],[[66575,66575],"mapped",[66615]],[[66576,66576],"mapped",[66616]],[[66577,66577],"mapped",[66617]],[[66578,66578],"mapped",[66618]],[[66579,66579],"mapped",[66619]],[[66580,66580],"mapped",[66620]],[[66581,66581],"mapped",[66621]],[[66582,66582],"mapped",[66622]],[[66583,66583],"mapped",[66623]],[[66584,66584],"mapped",[66624]],[[66585,66585],"mapped",[66625]],[[66586,66586],"mapped",[66626]],[[66587,66587],"mapped",[66627]],[[66588,66588],"mapped",[66628]],[[66589,66589],"mapped",[66629]],[[66590,66590],"mapped",[66630]],[[66591,66591],"mapped",[66631]],[[66592,66592],"mapped",[66632]],[[66593,66593],"mapped",[66633]],[[66594,66594],"mapped",[66634]],[[66595,66595],"mapped",[66635]],[[66596,66596],"mapped",[66636]],[[66597,66597],"mapped",[66637]],[[66598,66598],"mapped",[66638]],[[66599,66599],"mapped",[66639]],[[66600,66637],"valid"],[[66638,66717],"valid"],[[66718,66719],"disallowed"],[[66720,66729],"valid"],[[66730,66815],"disallowed"],[[66816,66855],"valid"],[[66856,66863],"disallowed"],[[66864,66915],"valid"],[[66916,66926],"disallowed"],[[66927,66927],"valid",[],"NV8"],[[66928,67071],"disallowed"],[[67072,67382],"valid"],[[67383,67391],"disallowed"],[[67392,67413],"valid"],[[67414,67423],"disallowed"],[[67424,67431],"valid"],[[67432,67583],"disallowed"],[[67584,67589],"valid"],[[67590,67591],"disallowed"],[[67592,67592],"valid"],[[67593,67593],"disallowed"],[[67594,67637],"valid"],[[67638,67638],"disallowed"],[[67639,67640],"valid"],[[67641,67643],"disallowed"],[[67644,67644],"valid"],[[67645,67646],"disallowed"],[[67647,67647],"valid"],[[67648,67669],"valid"],[[67670,67670],"disallowed"],[[67671,67679],"valid",[],"NV8"],[[67680,67702],"valid"],[[67703,67711],"valid",[],"NV8"],[[67712,67742],"valid"],[[67743,67750],"disallowed"],[[67751,67759],"valid",[],"NV8"],[[67760,67807],"disallowed"],[[67808,67826],"valid"],[[67827,67827],"disallowed"],[[67828,67829],"valid"],[[67830,67834],"disallowed"],[[67835,67839],"valid",[],"NV8"],[[67840,67861],"valid"],[[67862,67865],"valid",[],"NV8"],[[67866,67867],"valid",[],"NV8"],[[67868,67870],"disallowed"],[[67871,67871],"valid",[],"NV8"],[[67872,67897],"valid"],[[67898,67902],"disallowed"],[[67903,67903],"valid",[],"NV8"],[[67904,67967],"disallowed"],[[67968,68023],"valid"],[[68024,68027],"disallowed"],[[68028,68029],"valid",[],"NV8"],[[68030,68031],"valid"],[[68032,68047],"valid",[],"NV8"],[[68048,68049],"disallowed"],[[68050,68095],"valid",[],"NV8"],[[68096,68099],"valid"],[[68100,68100],"disallowed"],[[68101,68102],"valid"],[[68103,68107],"disallowed"],[[68108,68115],"valid"],[[68116,68116],"disallowed"],[[68117,68119],"valid"],[[68120,68120],"disallowed"],[[68121,68147],"valid"],[[68148,68151],"disallowed"],[[68152,68154],"valid"],[[68155,68158],"disallowed"],[[68159,68159],"valid"],[[68160,68167],"valid",[],"NV8"],[[68168,68175],"disallowed"],[[68176,68184],"valid",[],"NV8"],[[68185,68191],"disallowed"],[[68192,68220],"valid"],[[68221,68223],"valid",[],"NV8"],[[68224,68252],"valid"],[[68253,68255],"valid",[],"NV8"],[[68256,68287],"disallowed"],[[68288,68295],"valid"],[[68296,68296],"valid",[],"NV8"],[[68297,68326],"valid"],[[68327,68330],"disallowed"],[[68331,68342],"valid",[],"NV8"],[[68343,68351],"disallowed"],[[68352,68405],"valid"],[[68406,68408],"disallowed"],[[68409,68415],"valid",[],"NV8"],[[68416,68437],"valid"],[[68438,68439],"disallowed"],[[68440,68447],"valid",[],"NV8"],[[68448,68466],"valid"],[[68467,68471],"disallowed"],[[68472,68479],"valid",[],"NV8"],[[68480,68497],"valid"],[[68498,68504],"disallowed"],[[68505,68508],"valid",[],"NV8"],[[68509,68520],"disallowed"],[[68521,68527],"valid",[],"NV8"],[[68528,68607],"disallowed"],[[68608,68680],"valid"],[[68681,68735],"disallowed"],[[68736,68736],"mapped",[68800]],[[68737,68737],"mapped",[68801]],[[68738,68738],"mapped",[68802]],[[68739,68739],"mapped",[68803]],[[68740,68740],"mapped",[68804]],[[68741,68741],"mapped",[68805]],[[68742,68742],"mapped",[68806]],[[68743,68743],"mapped",[68807]],[[68744,68744],"mapped",[68808]],[[68745,68745],"mapped",[68809]],[[68746,68746],"mapped",[68810]],[[68747,68747],"mapped",[68811]],[[68748,68748],"mapped",[68812]],[[68749,68749],"mapped",[68813]],[[68750,68750],"mapped",[68814]],[[68751,68751],"mapped",[68815]],[[68752,68752],"mapped",[68816]],[[68753,68753],"mapped",[68817]],[[68754,68754],"mapped",[68818]],[[68755,68755],"mapped",[68819]],[[68756,68756],"mapped",[68820]],[[68757,68757],"mapped",[68821]],[[68758,68758],"mapped",[68822]],[[68759,68759],"mapped",[68823]],[[68760,68760],"mapped",[68824]],[[68761,68761],"mapped",[68825]],[[68762,68762],"mapped",[68826]],[[68763,68763],"mapped",[68827]],[[68764,68764],"mapped",[68828]],[[68765,68765],"mapped",[68829]],[[68766,68766],"mapped",[68830]],[[68767,68767],"mapped",[68831]],[[68768,68768],"mapped",[68832]],[[68769,68769],"mapped",[68833]],[[68770,68770],"mapped",[68834]],[[68771,68771],"mapped",[68835]],[[68772,68772],"mapped",[68836]],[[68773,68773],"mapped",[68837]],[[68774,68774],"mapped",[68838]],[[68775,68775],"mapped",[68839]],[[68776,68776],"mapped",[68840]],[[68777,68777],"mapped",[68841]],[[68778,68778],"mapped",[68842]],[[68779,68779],"mapped",[68843]],[[68780,68780],"mapped",[68844]],[[68781,68781],"mapped",[68845]],[[68782,68782],"mapped",[68846]],[[68783,68783],"mapped",[68847]],[[68784,68784],"mapped",[68848]],[[68785,68785],"mapped",[68849]],[[68786,68786],"mapped",[68850]],[[68787,68799],"disallowed"],[[68800,68850],"valid"],[[68851,68857],"disallowed"],[[68858,68863],"valid",[],"NV8"],[[68864,69215],"disallowed"],[[69216,69246],"valid",[],"NV8"],[[69247,69631],"disallowed"],[[69632,69702],"valid"],[[69703,69709],"valid",[],"NV8"],[[69710,69713],"disallowed"],[[69714,69733],"valid",[],"NV8"],[[69734,69743],"valid"],[[69744,69758],"disallowed"],[[69759,69759],"valid"],[[69760,69818],"valid"],[[69819,69820],"valid",[],"NV8"],[[69821,69821],"disallowed"],[[69822,69825],"valid",[],"NV8"],[[69826,69839],"disallowed"],[[69840,69864],"valid"],[[69865,69871],"disallowed"],[[69872,69881],"valid"],[[69882,69887],"disallowed"],[[69888,69940],"valid"],[[69941,69941],"disallowed"],[[69942,69951],"valid"],[[69952,69955],"valid",[],"NV8"],[[69956,69967],"disallowed"],[[69968,70003],"valid"],[[70004,70005],"valid",[],"NV8"],[[70006,70006],"valid"],[[70007,70015],"disallowed"],[[70016,70084],"valid"],[[70085,70088],"valid",[],"NV8"],[[70089,70089],"valid",[],"NV8"],[[70090,70092],"valid"],[[70093,70093],"valid",[],"NV8"],[[70094,70095],"disallowed"],[[70096,70105],"valid"],[[70106,70106],"valid"],[[70107,70107],"valid",[],"NV8"],[[70108,70108],"valid"],[[70109,70111],"valid",[],"NV8"],[[70112,70112],"disallowed"],[[70113,70132],"valid",[],"NV8"],[[70133,70143],"disallowed"],[[70144,70161],"valid"],[[70162,70162],"disallowed"],[[70163,70199],"valid"],[[70200,70205],"valid",[],"NV8"],[[70206,70271],"disallowed"],[[70272,70278],"valid"],[[70279,70279],"disallowed"],[[70280,70280],"valid"],[[70281,70281],"disallowed"],[[70282,70285],"valid"],[[70286,70286],"disallowed"],[[70287,70301],"valid"],[[70302,70302],"disallowed"],[[70303,70312],"valid"],[[70313,70313],"valid",[],"NV8"],[[70314,70319],"disallowed"],[[70320,70378],"valid"],[[70379,70383],"disallowed"],[[70384,70393],"valid"],[[70394,70399],"disallowed"],[[70400,70400],"valid"],[[70401,70403],"valid"],[[70404,70404],"disallowed"],[[70405,70412],"valid"],[[70413,70414],"disallowed"],[[70415,70416],"valid"],[[70417,70418],"disallowed"],[[70419,70440],"valid"],[[70441,70441],"disallowed"],[[70442,70448],"valid"],[[70449,70449],"disallowed"],[[70450,70451],"valid"],[[70452,70452],"disallowed"],[[70453,70457],"valid"],[[70458,70459],"disallowed"],[[70460,70468],"valid"],[[70469,70470],"disallowed"],[[70471,70472],"valid"],[[70473,70474],"disallowed"],[[70475,70477],"valid"],[[70478,70479],"disallowed"],[[70480,70480],"valid"],[[70481,70486],"disallowed"],[[70487,70487],"valid"],[[70488,70492],"disallowed"],[[70493,70499],"valid"],[[70500,70501],"disallowed"],[[70502,70508],"valid"],[[70509,70511],"disallowed"],[[70512,70516],"valid"],[[70517,70783],"disallowed"],[[70784,70853],"valid"],[[70854,70854],"valid",[],"NV8"],[[70855,70855],"valid"],[[70856,70863],"disallowed"],[[70864,70873],"valid"],[[70874,71039],"disallowed"],[[71040,71093],"valid"],[[71094,71095],"disallowed"],[[71096,71104],"valid"],[[71105,71113],"valid",[],"NV8"],[[71114,71127],"valid",[],"NV8"],[[71128,71133],"valid"],[[71134,71167],"disallowed"],[[71168,71232],"valid"],[[71233,71235],"valid",[],"NV8"],[[71236,71236],"valid"],[[71237,71247],"disallowed"],[[71248,71257],"valid"],[[71258,71295],"disallowed"],[[71296,71351],"valid"],[[71352,71359],"disallowed"],[[71360,71369],"valid"],[[71370,71423],"disallowed"],[[71424,71449],"valid"],[[71450,71452],"disallowed"],[[71453,71467],"valid"],[[71468,71471],"disallowed"],[[71472,71481],"valid"],[[71482,71487],"valid",[],"NV8"],[[71488,71839],"disallowed"],[[71840,71840],"mapped",[71872]],[[71841,71841],"mapped",[71873]],[[71842,71842],"mapped",[71874]],[[71843,71843],"mapped",[71875]],[[71844,71844],"mapped",[71876]],[[71845,71845],"mapped",[71877]],[[71846,71846],"mapped",[71878]],[[71847,71847],"mapped",[71879]],[[71848,71848],"mapped",[71880]],[[71849,71849],"mapped",[71881]],[[71850,71850],"mapped",[71882]],[[71851,71851],"mapped",[71883]],[[71852,71852],"mapped",[71884]],[[71853,71853],"mapped",[71885]],[[71854,71854],"mapped",[71886]],[[71855,71855],"mapped",[71887]],[[71856,71856],"mapped",[71888]],[[71857,71857],"mapped",[71889]],[[71858,71858],"mapped",[71890]],[[71859,71859],"mapped",[71891]],[[71860,71860],"mapped",[71892]],[[71861,71861],"mapped",[71893]],[[71862,71862],"mapped",[71894]],[[71863,71863],"mapped",[71895]],[[71864,71864],"mapped",[71896]],[[71865,71865],"mapped",[71897]],[[71866,71866],"mapped",[71898]],[[71867,71867],"mapped",[71899]],[[71868,71868],"mapped",[71900]],[[71869,71869],"mapped",[71901]],[[71870,71870],"mapped",[71902]],[[71871,71871],"mapped",[71903]],[[71872,71913],"valid"],[[71914,71922],"valid",[],"NV8"],[[71923,71934],"disallowed"],[[71935,71935],"valid"],[[71936,72383],"disallowed"],[[72384,72440],"valid"],[[72441,73727],"disallowed"],[[73728,74606],"valid"],[[74607,74648],"valid"],[[74649,74649],"valid"],[[74650,74751],"disallowed"],[[74752,74850],"valid",[],"NV8"],[[74851,74862],"valid",[],"NV8"],[[74863,74863],"disallowed"],[[74864,74867],"valid",[],"NV8"],[[74868,74868],"valid",[],"NV8"],[[74869,74879],"disallowed"],[[74880,75075],"valid"],[[75076,77823],"disallowed"],[[77824,78894],"valid"],[[78895,82943],"disallowed"],[[82944,83526],"valid"],[[83527,92159],"disallowed"],[[92160,92728],"valid"],[[92729,92735],"disallowed"],[[92736,92766],"valid"],[[92767,92767],"disallowed"],[[92768,92777],"valid"],[[92778,92781],"disallowed"],[[92782,92783],"valid",[],"NV8"],[[92784,92879],"disallowed"],[[92880,92909],"valid"],[[92910,92911],"disallowed"],[[92912,92916],"valid"],[[92917,92917],"valid",[],"NV8"],[[92918,92927],"disallowed"],[[92928,92982],"valid"],[[92983,92991],"valid",[],"NV8"],[[92992,92995],"valid"],[[92996,92997],"valid",[],"NV8"],[[92998,93007],"disallowed"],[[93008,93017],"valid"],[[93018,93018],"disallowed"],[[93019,93025],"valid",[],"NV8"],[[93026,93026],"disallowed"],[[93027,93047],"valid"],[[93048,93052],"disallowed"],[[93053,93071],"valid"],[[93072,93951],"disallowed"],[[93952,94020],"valid"],[[94021,94031],"disallowed"],[[94032,94078],"valid"],[[94079,94094],"disallowed"],[[94095,94111],"valid"],[[94112,110591],"disallowed"],[[110592,110593],"valid"],[[110594,113663],"disallowed"],[[113664,113770],"valid"],[[113771,113775],"disallowed"],[[113776,113788],"valid"],[[113789,113791],"disallowed"],[[113792,113800],"valid"],[[113801,113807],"disallowed"],[[113808,113817],"valid"],[[113818,113819],"disallowed"],[[113820,113820],"valid",[],"NV8"],[[113821,113822],"valid"],[[113823,113823],"valid",[],"NV8"],[[113824,113827],"ignored"],[[113828,118783],"disallowed"],[[118784,119029],"valid",[],"NV8"],[[119030,119039],"disallowed"],[[119040,119078],"valid",[],"NV8"],[[119079,119080],"disallowed"],[[119081,119081],"valid",[],"NV8"],[[119082,119133],"valid",[],"NV8"],[[119134,119134],"mapped",[119127,119141]],[[119135,119135],"mapped",[119128,119141]],[[119136,119136],"mapped",[119128,119141,119150]],[[119137,119137],"mapped",[119128,119141,119151]],[[119138,119138],"mapped",[119128,119141,119152]],[[119139,119139],"mapped",[119128,119141,119153]],[[119140,119140],"mapped",[119128,119141,119154]],[[119141,119154],"valid",[],"NV8"],[[119155,119162],"disallowed"],[[119163,119226],"valid",[],"NV8"],[[119227,119227],"mapped",[119225,119141]],[[119228,119228],"mapped",[119226,119141]],[[119229,119229],"mapped",[119225,119141,119150]],[[119230,119230],"mapped",[119226,119141,119150]],[[119231,119231],"mapped",[119225,119141,119151]],[[119232,119232],"mapped",[119226,119141,119151]],[[119233,119261],"valid",[],"NV8"],[[119262,119272],"valid",[],"NV8"],[[119273,119295],"disallowed"],[[119296,119365],"valid",[],"NV8"],[[119366,119551],"disallowed"],[[119552,119638],"valid",[],"NV8"],[[119639,119647],"disallowed"],[[119648,119665],"valid",[],"NV8"],[[119666,119807],"disallowed"],[[119808,119808],"mapped",[97]],[[119809,119809],"mapped",[98]],[[119810,119810],"mapped",[99]],[[119811,119811],"mapped",[100]],[[119812,119812],"mapped",[101]],[[119813,119813],"mapped",[102]],[[119814,119814],"mapped",[103]],[[119815,119815],"mapped",[104]],[[119816,119816],"mapped",[105]],[[119817,119817],"mapped",[106]],[[119818,119818],"mapped",[107]],[[119819,119819],"mapped",[108]],[[119820,119820],"mapped",[109]],[[119821,119821],"mapped",[110]],[[119822,119822],"mapped",[111]],[[119823,119823],"mapped",[112]],[[119824,119824],"mapped",[113]],[[119825,119825],"mapped",[114]],[[119826,119826],"mapped",[115]],[[119827,119827],"mapped",[116]],[[119828,119828],"mapped",[117]],[[119829,119829],"mapped",[118]],[[119830,119830],"mapped",[119]],[[119831,119831],"mapped",[120]],[[119832,119832],"mapped",[121]],[[119833,119833],"mapped",[122]],[[119834,119834],"mapped",[97]],[[119835,119835],"mapped",[98]],[[119836,119836],"mapped",[99]],[[119837,119837],"mapped",[100]],[[119838,119838],"mapped",[101]],[[119839,119839],"mapped",[102]],[[119840,119840],"mapped",[103]],[[119841,119841],"mapped",[104]],[[119842,119842],"mapped",[105]],[[119843,119843],"mapped",[106]],[[119844,119844],"mapped",[107]],[[119845,119845],"mapped",[108]],[[119846,119846],"mapped",[109]],[[119847,119847],"mapped",[110]],[[119848,119848],"mapped",[111]],[[119849,119849],"mapped",[112]],[[119850,119850],"mapped",[113]],[[119851,119851],"mapped",[114]],[[119852,119852],"mapped",[115]],[[119853,119853],"mapped",[116]],[[119854,119854],"mapped",[117]],[[119855,119855],"mapped",[118]],[[119856,119856],"mapped",[119]],[[119857,119857],"mapped",[120]],[[119858,119858],"mapped",[121]],[[119859,119859],"mapped",[122]],[[119860,119860],"mapped",[97]],[[119861,119861],"mapped",[98]],[[119862,119862],"mapped",[99]],[[119863,119863],"mapped",[100]],[[119864,119864],"mapped",[101]],[[119865,119865],"mapped",[102]],[[119866,119866],"mapped",[103]],[[119867,119867],"mapped",[104]],[[119868,119868],"mapped",[105]],[[119869,119869],"mapped",[106]],[[119870,119870],"mapped",[107]],[[119871,119871],"mapped",[108]],[[119872,119872],"mapped",[109]],[[119873,119873],"mapped",[110]],[[119874,119874],"mapped",[111]],[[119875,119875],"mapped",[112]],[[119876,119876],"mapped",[113]],[[119877,119877],"mapped",[114]],[[119878,119878],"mapped",[115]],[[119879,119879],"mapped",[116]],[[119880,119880],"mapped",[117]],[[119881,119881],"mapped",[118]],[[119882,119882],"mapped",[119]],[[119883,119883],"mapped",[120]],[[119884,119884],"mapped",[121]],[[119885,119885],"mapped",[122]],[[119886,119886],"mapped",[97]],[[119887,119887],"mapped",[98]],[[119888,119888],"mapped",[99]],[[119889,119889],"mapped",[100]],[[119890,119890],"mapped",[101]],[[119891,119891],"mapped",[102]],[[119892,119892],"mapped",[103]],[[119893,119893],"disallowed"],[[119894,119894],"mapped",[105]],[[119895,119895],"mapped",[106]],[[119896,119896],"mapped",[107]],[[119897,119897],"mapped",[108]],[[119898,119898],"mapped",[109]],[[119899,119899],"mapped",[110]],[[119900,119900],"mapped",[111]],[[119901,119901],"mapped",[112]],[[119902,119902],"mapped",[113]],[[119903,119903],"mapped",[114]],[[119904,119904],"mapped",[115]],[[119905,119905],"mapped",[116]],[[119906,119906],"mapped",[117]],[[119907,119907],"mapped",[118]],[[119908,119908],"mapped",[119]],[[119909,119909],"mapped",[120]],[[119910,119910],"mapped",[121]],[[119911,119911],"mapped",[122]],[[119912,119912],"mapped",[97]],[[119913,119913],"mapped",[98]],[[119914,119914],"mapped",[99]],[[119915,119915],"mapped",[100]],[[119916,119916],"mapped",[101]],[[119917,119917],"mapped",[102]],[[119918,119918],"mapped",[103]],[[119919,119919],"mapped",[104]],[[119920,119920],"mapped",[105]],[[119921,119921],"mapped",[106]],[[119922,119922],"mapped",[107]],[[119923,119923],"mapped",[108]],[[119924,119924],"mapped",[109]],[[119925,119925],"mapped",[110]],[[119926,119926],"mapped",[111]],[[119927,119927],"mapped",[112]],[[119928,119928],"mapped",[113]],[[119929,119929],"mapped",[114]],[[119930,119930],"mapped",[115]],[[119931,119931],"mapped",[116]],[[119932,119932],"mapped",[117]],[[119933,119933],"mapped",[118]],[[119934,119934],"mapped",[119]],[[119935,119935],"mapped",[120]],[[119936,119936],"mapped",[121]],[[119937,119937],"mapped",[122]],[[119938,119938],"mapped",[97]],[[119939,119939],"mapped",[98]],[[119940,119940],"mapped",[99]],[[119941,119941],"mapped",[100]],[[119942,119942],"mapped",[101]],[[119943,119943],"mapped",[102]],[[119944,119944],"mapped",[103]],[[119945,119945],"mapped",[104]],[[119946,119946],"mapped",[105]],[[119947,119947],"mapped",[106]],[[119948,119948],"mapped",[107]],[[119949,119949],"mapped",[108]],[[119950,119950],"mapped",[109]],[[119951,119951],"mapped",[110]],[[119952,119952],"mapped",[111]],[[119953,119953],"mapped",[112]],[[119954,119954],"mapped",[113]],[[119955,119955],"mapped",[114]],[[119956,119956],"mapped",[115]],[[119957,119957],"mapped",[116]],[[119958,119958],"mapped",[117]],[[119959,119959],"mapped",[118]],[[119960,119960],"mapped",[119]],[[119961,119961],"mapped",[120]],[[119962,119962],"mapped",[121]],[[119963,119963],"mapped",[122]],[[119964,119964],"mapped",[97]],[[119965,119965],"disallowed"],[[119966,119966],"mapped",[99]],[[119967,119967],"mapped",[100]],[[119968,119969],"disallowed"],[[119970,119970],"mapped",[103]],[[119971,119972],"disallowed"],[[119973,119973],"mapped",[106]],[[119974,119974],"mapped",[107]],[[119975,119976],"disallowed"],[[119977,119977],"mapped",[110]],[[119978,119978],"mapped",[111]],[[119979,119979],"mapped",[112]],[[119980,119980],"mapped",[113]],[[119981,119981],"disallowed"],[[119982,119982],"mapped",[115]],[[119983,119983],"mapped",[116]],[[119984,119984],"mapped",[117]],[[119985,119985],"mapped",[118]],[[119986,119986],"mapped",[119]],[[119987,119987],"mapped",[120]],[[119988,119988],"mapped",[121]],[[119989,119989],"mapped",[122]],[[119990,119990],"mapped",[97]],[[119991,119991],"mapped",[98]],[[119992,119992],"mapped",[99]],[[119993,119993],"mapped",[100]],[[119994,119994],"disallowed"],[[119995,119995],"mapped",[102]],[[119996,119996],"disallowed"],[[119997,119997],"mapped",[104]],[[119998,119998],"mapped",[105]],[[119999,119999],"mapped",[106]],[[120000,120000],"mapped",[107]],[[120001,120001],"mapped",[108]],[[120002,120002],"mapped",[109]],[[120003,120003],"mapped",[110]],[[120004,120004],"disallowed"],[[120005,120005],"mapped",[112]],[[120006,120006],"mapped",[113]],[[120007,120007],"mapped",[114]],[[120008,120008],"mapped",[115]],[[120009,120009],"mapped",[116]],[[120010,120010],"mapped",[117]],[[120011,120011],"mapped",[118]],[[120012,120012],"mapped",[119]],[[120013,120013],"mapped",[120]],[[120014,120014],"mapped",[121]],[[120015,120015],"mapped",[122]],[[120016,120016],"mapped",[97]],[[120017,120017],"mapped",[98]],[[120018,120018],"mapped",[99]],[[120019,120019],"mapped",[100]],[[120020,120020],"mapped",[101]],[[120021,120021],"mapped",[102]],[[120022,120022],"mapped",[103]],[[120023,120023],"mapped",[104]],[[120024,120024],"mapped",[105]],[[120025,120025],"mapped",[106]],[[120026,120026],"mapped",[107]],[[120027,120027],"mapped",[108]],[[120028,120028],"mapped",[109]],[[120029,120029],"mapped",[110]],[[120030,120030],"mapped",[111]],[[120031,120031],"mapped",[112]],[[120032,120032],"mapped",[113]],[[120033,120033],"mapped",[114]],[[120034,120034],"mapped",[115]],[[120035,120035],"mapped",[116]],[[120036,120036],"mapped",[117]],[[120037,120037],"mapped",[118]],[[120038,120038],"mapped",[119]],[[120039,120039],"mapped",[120]],[[120040,120040],"mapped",[121]],[[120041,120041],"mapped",[122]],[[120042,120042],"mapped",[97]],[[120043,120043],"mapped",[98]],[[120044,120044],"mapped",[99]],[[120045,120045],"mapped",[100]],[[120046,120046],"mapped",[101]],[[120047,120047],"mapped",[102]],[[120048,120048],"mapped",[103]],[[120049,120049],"mapped",[104]],[[120050,120050],"mapped",[105]],[[120051,120051],"mapped",[106]],[[120052,120052],"mapped",[107]],[[120053,120053],"mapped",[108]],[[120054,120054],"mapped",[109]],[[120055,120055],"mapped",[110]],[[120056,120056],"mapped",[111]],[[120057,120057],"mapped",[112]],[[120058,120058],"mapped",[113]],[[120059,120059],"mapped",[114]],[[120060,120060],"mapped",[115]],[[120061,120061],"mapped",[116]],[[120062,120062],"mapped",[117]],[[120063,120063],"mapped",[118]],[[120064,120064],"mapped",[119]],[[120065,120065],"mapped",[120]],[[120066,120066],"mapped",[121]],[[120067,120067],"mapped",[122]],[[120068,120068],"mapped",[97]],[[120069,120069],"mapped",[98]],[[120070,120070],"disallowed"],[[120071,120071],"mapped",[100]],[[120072,120072],"mapped",[101]],[[120073,120073],"mapped",[102]],[[120074,120074],"mapped",[103]],[[120075,120076],"disallowed"],[[120077,120077],"mapped",[106]],[[120078,120078],"mapped",[107]],[[120079,120079],"mapped",[108]],[[120080,120080],"mapped",[109]],[[120081,120081],"mapped",[110]],[[120082,120082],"mapped",[111]],[[120083,120083],"mapped",[112]],[[120084,120084],"mapped",[113]],[[120085,120085],"disallowed"],[[120086,120086],"mapped",[115]],[[120087,120087],"mapped",[116]],[[120088,120088],"mapped",[117]],[[120089,120089],"mapped",[118]],[[120090,120090],"mapped",[119]],[[120091,120091],"mapped",[120]],[[120092,120092],"mapped",[121]],[[120093,120093],"disallowed"],[[120094,120094],"mapped",[97]],[[120095,120095],"mapped",[98]],[[120096,120096],"mapped",[99]],[[120097,120097],"mapped",[100]],[[120098,120098],"mapped",[101]],[[120099,120099],"mapped",[102]],[[120100,120100],"mapped",[103]],[[120101,120101],"mapped",[104]],[[120102,120102],"mapped",[105]],[[120103,120103],"mapped",[106]],[[120104,120104],"mapped",[107]],[[120105,120105],"mapped",[108]],[[120106,120106],"mapped",[109]],[[120107,120107],"mapped",[110]],[[120108,120108],"mapped",[111]],[[120109,120109],"mapped",[112]],[[120110,120110],"mapped",[113]],[[120111,120111],"mapped",[114]],[[120112,120112],"mapped",[115]],[[120113,120113],"mapped",[116]],[[120114,120114],"mapped",[117]],[[120115,120115],"mapped",[118]],[[120116,120116],"mapped",[119]],[[120117,120117],"mapped",[120]],[[120118,120118],"mapped",[121]],[[120119,120119],"mapped",[122]],[[120120,120120],"mapped",[97]],[[120121,120121],"mapped",[98]],[[120122,120122],"disallowed"],[[120123,120123],"mapped",[100]],[[120124,120124],"mapped",[101]],[[120125,120125],"mapped",[102]],[[120126,120126],"mapped",[103]],[[120127,120127],"disallowed"],[[120128,120128],"mapped",[105]],[[120129,120129],"mapped",[106]],[[120130,120130],"mapped",[107]],[[120131,120131],"mapped",[108]],[[120132,120132],"mapped",[109]],[[120133,120133],"disallowed"],[[120134,120134],"mapped",[111]],[[120135,120137],"disallowed"],[[120138,120138],"mapped",[115]],[[120139,120139],"mapped",[116]],[[120140,120140],"mapped",[117]],[[120141,120141],"mapped",[118]],[[120142,120142],"mapped",[119]],[[120143,120143],"mapped",[120]],[[120144,120144],"mapped",[121]],[[120145,120145],"disallowed"],[[120146,120146],"mapped",[97]],[[120147,120147],"mapped",[98]],[[120148,120148],"mapped",[99]],[[120149,120149],"mapped",[100]],[[120150,120150],"mapped",[101]],[[120151,120151],"mapped",[102]],[[120152,120152],"mapped",[103]],[[120153,120153],"mapped",[104]],[[120154,120154],"mapped",[105]],[[120155,120155],"mapped",[106]],[[120156,120156],"mapped",[107]],[[120157,120157],"mapped",[108]],[[120158,120158],"mapped",[109]],[[120159,120159],"mapped",[110]],[[120160,120160],"mapped",[111]],[[120161,120161],"mapped",[112]],[[120162,120162],"mapped",[113]],[[120163,120163],"mapped",[114]],[[120164,120164],"mapped",[115]],[[120165,120165],"mapped",[116]],[[120166,120166],"mapped",[117]],[[120167,120167],"mapped",[118]],[[120168,120168],"mapped",[119]],[[120169,120169],"mapped",[120]],[[120170,120170],"mapped",[121]],[[120171,120171],"mapped",[122]],[[120172,120172],"mapped",[97]],[[120173,120173],"mapped",[98]],[[120174,120174],"mapped",[99]],[[120175,120175],"mapped",[100]],[[120176,120176],"mapped",[101]],[[120177,120177],"mapped",[102]],[[120178,120178],"mapped",[103]],[[120179,120179],"mapped",[104]],[[120180,120180],"mapped",[105]],[[120181,120181],"mapped",[106]],[[120182,120182],"mapped",[107]],[[120183,120183],"mapped",[108]],[[120184,120184],"mapped",[109]],[[120185,120185],"mapped",[110]],[[120186,120186],"mapped",[111]],[[120187,120187],"mapped",[112]],[[120188,120188],"mapped",[113]],[[120189,120189],"mapped",[114]],[[120190,120190],"mapped",[115]],[[120191,120191],"mapped",[116]],[[120192,120192],"mapped",[117]],[[120193,120193],"mapped",[118]],[[120194,120194],"mapped",[119]],[[120195,120195],"mapped",[120]],[[120196,120196],"mapped",[121]],[[120197,120197],"mapped",[122]],[[120198,120198],"mapped",[97]],[[120199,120199],"mapped",[98]],[[120200,120200],"mapped",[99]],[[120201,120201],"mapped",[100]],[[120202,120202],"mapped",[101]],[[120203,120203],"mapped",[102]],[[120204,120204],"mapped",[103]],[[120205,120205],"mapped",[104]],[[120206,120206],"mapped",[105]],[[120207,120207],"mapped",[106]],[[120208,120208],"mapped",[107]],[[120209,120209],"mapped",[108]],[[120210,120210],"mapped",[109]],[[120211,120211],"mapped",[110]],[[120212,120212],"mapped",[111]],[[120213,120213],"mapped",[112]],[[120214,120214],"mapped",[113]],[[120215,120215],"mapped",[114]],[[120216,120216],"mapped",[115]],[[120217,120217],"mapped",[116]],[[120218,120218],"mapped",[117]],[[120219,120219],"mapped",[118]],[[120220,120220],"mapped",[119]],[[120221,120221],"mapped",[120]],[[120222,120222],"mapped",[121]],[[120223,120223],"mapped",[122]],[[120224,120224],"mapped",[97]],[[120225,120225],"mapped",[98]],[[120226,120226],"mapped",[99]],[[120227,120227],"mapped",[100]],[[120228,120228],"mapped",[101]],[[120229,120229],"mapped",[102]],[[120230,120230],"mapped",[103]],[[120231,120231],"mapped",[104]],[[120232,120232],"mapped",[105]],[[120233,120233],"mapped",[106]],[[120234,120234],"mapped",[107]],[[120235,120235],"mapped",[108]],[[120236,120236],"mapped",[109]],[[120237,120237],"mapped",[110]],[[120238,120238],"mapped",[111]],[[120239,120239],"mapped",[112]],[[120240,120240],"mapped",[113]],[[120241,120241],"mapped",[114]],[[120242,120242],"mapped",[115]],[[120243,120243],"mapped",[116]],[[120244,120244],"mapped",[117]],[[120245,120245],"mapped",[118]],[[120246,120246],"mapped",[119]],[[120247,120247],"mapped",[120]],[[120248,120248],"mapped",[121]],[[120249,120249],"mapped",[122]],[[120250,120250],"mapped",[97]],[[120251,120251],"mapped",[98]],[[120252,120252],"mapped",[99]],[[120253,120253],"mapped",[100]],[[120254,120254],"mapped",[101]],[[120255,120255],"mapped",[102]],[[120256,120256],"mapped",[103]],[[120257,120257],"mapped",[104]],[[120258,120258],"mapped",[105]],[[120259,120259],"mapped",[106]],[[120260,120260],"mapped",[107]],[[120261,120261],"mapped",[108]],[[120262,120262],"mapped",[109]],[[120263,120263],"mapped",[110]],[[120264,120264],"mapped",[111]],[[120265,120265],"mapped",[112]],[[120266,120266],"mapped",[113]],[[120267,120267],"mapped",[114]],[[120268,120268],"mapped",[115]],[[120269,120269],"mapped",[116]],[[120270,120270],"mapped",[117]],[[120271,120271],"mapped",[118]],[[120272,120272],"mapped",[119]],[[120273,120273],"mapped",[120]],[[120274,120274],"mapped",[121]],[[120275,120275],"mapped",[122]],[[120276,120276],"mapped",[97]],[[120277,120277],"mapped",[98]],[[120278,120278],"mapped",[99]],[[120279,120279],"mapped",[100]],[[120280,120280],"mapped",[101]],[[120281,120281],"mapped",[102]],[[120282,120282],"mapped",[103]],[[120283,120283],"mapped",[104]],[[120284,120284],"mapped",[105]],[[120285,120285],"mapped",[106]],[[120286,120286],"mapped",[107]],[[120287,120287],"mapped",[108]],[[120288,120288],"mapped",[109]],[[120289,120289],"mapped",[110]],[[120290,120290],"mapped",[111]],[[120291,120291],"mapped",[112]],[[120292,120292],"mapped",[113]],[[120293,120293],"mapped",[114]],[[120294,120294],"mapped",[115]],[[120295,120295],"mapped",[116]],[[120296,120296],"mapped",[117]],[[120297,120297],"mapped",[118]],[[120298,120298],"mapped",[119]],[[120299,120299],"mapped",[120]],[[120300,120300],"mapped",[121]],[[120301,120301],"mapped",[122]],[[120302,120302],"mapped",[97]],[[120303,120303],"mapped",[98]],[[120304,120304],"mapped",[99]],[[120305,120305],"mapped",[100]],[[120306,120306],"mapped",[101]],[[120307,120307],"mapped",[102]],[[120308,120308],"mapped",[103]],[[120309,120309],"mapped",[104]],[[120310,120310],"mapped",[105]],[[120311,120311],"mapped",[106]],[[120312,120312],"mapped",[107]],[[120313,120313],"mapped",[108]],[[120314,120314],"mapped",[109]],[[120315,120315],"mapped",[110]],[[120316,120316],"mapped",[111]],[[120317,120317],"mapped",[112]],[[120318,120318],"mapped",[113]],[[120319,120319],"mapped",[114]],[[120320,120320],"mapped",[115]],[[120321,120321],"mapped",[116]],[[120322,120322],"mapped",[117]],[[120323,120323],"mapped",[118]],[[120324,120324],"mapped",[119]],[[120325,120325],"mapped",[120]],[[120326,120326],"mapped",[121]],[[120327,120327],"mapped",[122]],[[120328,120328],"mapped",[97]],[[120329,120329],"mapped",[98]],[[120330,120330],"mapped",[99]],[[120331,120331],"mapped",[100]],[[120332,120332],"mapped",[101]],[[120333,120333],"mapped",[102]],[[120334,120334],"mapped",[103]],[[120335,120335],"mapped",[104]],[[120336,120336],"mapped",[105]],[[120337,120337],"mapped",[106]],[[120338,120338],"mapped",[107]],[[120339,120339],"mapped",[108]],[[120340,120340],"mapped",[109]],[[120341,120341],"mapped",[110]],[[120342,120342],"mapped",[111]],[[120343,120343],"mapped",[112]],[[120344,120344],"mapped",[113]],[[120345,120345],"mapped",[114]],[[120346,120346],"mapped",[115]],[[120347,120347],"mapped",[116]],[[120348,120348],"mapped",[117]],[[120349,120349],"mapped",[118]],[[120350,120350],"mapped",[119]],[[120351,120351],"mapped",[120]],[[120352,120352],"mapped",[121]],[[120353,120353],"mapped",[122]],[[120354,120354],"mapped",[97]],[[120355,120355],"mapped",[98]],[[120356,120356],"mapped",[99]],[[120357,120357],"mapped",[100]],[[120358,120358],"mapped",[101]],[[120359,120359],"mapped",[102]],[[120360,120360],"mapped",[103]],[[120361,120361],"mapped",[104]],[[120362,120362],"mapped",[105]],[[120363,120363],"mapped",[106]],[[120364,120364],"mapped",[107]],[[120365,120365],"mapped",[108]],[[120366,120366],"mapped",[109]],[[120367,120367],"mapped",[110]],[[120368,120368],"mapped",[111]],[[120369,120369],"mapped",[112]],[[120370,120370],"mapped",[113]],[[120371,120371],"mapped",[114]],[[120372,120372],"mapped",[115]],[[120373,120373],"mapped",[116]],[[120374,120374],"mapped",[117]],[[120375,120375],"mapped",[118]],[[120376,120376],"mapped",[119]],[[120377,120377],"mapped",[120]],[[120378,120378],"mapped",[121]],[[120379,120379],"mapped",[122]],[[120380,120380],"mapped",[97]],[[120381,120381],"mapped",[98]],[[120382,120382],"mapped",[99]],[[120383,120383],"mapped",[100]],[[120384,120384],"mapped",[101]],[[120385,120385],"mapped",[102]],[[120386,120386],"mapped",[103]],[[120387,120387],"mapped",[104]],[[120388,120388],"mapped",[105]],[[120389,120389],"mapped",[106]],[[120390,120390],"mapped",[107]],[[120391,120391],"mapped",[108]],[[120392,120392],"mapped",[109]],[[120393,120393],"mapped",[110]],[[120394,120394],"mapped",[111]],[[120395,120395],"mapped",[112]],[[120396,120396],"mapped",[113]],[[120397,120397],"mapped",[114]],[[120398,120398],"mapped",[115]],[[120399,120399],"mapped",[116]],[[120400,120400],"mapped",[117]],[[120401,120401],"mapped",[118]],[[120402,120402],"mapped",[119]],[[120403,120403],"mapped",[120]],[[120404,120404],"mapped",[121]],[[120405,120405],"mapped",[122]],[[120406,120406],"mapped",[97]],[[120407,120407],"mapped",[98]],[[120408,120408],"mapped",[99]],[[120409,120409],"mapped",[100]],[[120410,120410],"mapped",[101]],[[120411,120411],"mapped",[102]],[[120412,120412],"mapped",[103]],[[120413,120413],"mapped",[104]],[[120414,120414],"mapped",[105]],[[120415,120415],"mapped",[106]],[[120416,120416],"mapped",[107]],[[120417,120417],"mapped",[108]],[[120418,120418],"mapped",[109]],[[120419,120419],"mapped",[110]],[[120420,120420],"mapped",[111]],[[120421,120421],"mapped",[112]],[[120422,120422],"mapped",[113]],[[120423,120423],"mapped",[114]],[[120424,120424],"mapped",[115]],[[120425,120425],"mapped",[116]],[[120426,120426],"mapped",[117]],[[120427,120427],"mapped",[118]],[[120428,120428],"mapped",[119]],[[120429,120429],"mapped",[120]],[[120430,120430],"mapped",[121]],[[120431,120431],"mapped",[122]],[[120432,120432],"mapped",[97]],[[120433,120433],"mapped",[98]],[[120434,120434],"mapped",[99]],[[120435,120435],"mapped",[100]],[[120436,120436],"mapped",[101]],[[120437,120437],"mapped",[102]],[[120438,120438],"mapped",[103]],[[120439,120439],"mapped",[104]],[[120440,120440],"mapped",[105]],[[120441,120441],"mapped",[106]],[[120442,120442],"mapped",[107]],[[120443,120443],"mapped",[108]],[[120444,120444],"mapped",[109]],[[120445,120445],"mapped",[110]],[[120446,120446],"mapped",[111]],[[120447,120447],"mapped",[112]],[[120448,120448],"mapped",[113]],[[120449,120449],"mapped",[114]],[[120450,120450],"mapped",[115]],[[120451,120451],"mapped",[116]],[[120452,120452],"mapped",[117]],[[120453,120453],"mapped",[118]],[[120454,120454],"mapped",[119]],[[120455,120455],"mapped",[120]],[[120456,120456],"mapped",[121]],[[120457,120457],"mapped",[122]],[[120458,120458],"mapped",[97]],[[120459,120459],"mapped",[98]],[[120460,120460],"mapped",[99]],[[120461,120461],"mapped",[100]],[[120462,120462],"mapped",[101]],[[120463,120463],"mapped",[102]],[[120464,120464],"mapped",[103]],[[120465,120465],"mapped",[104]],[[120466,120466],"mapped",[105]],[[120467,120467],"mapped",[106]],[[120468,120468],"mapped",[107]],[[120469,120469],"mapped",[108]],[[120470,120470],"mapped",[109]],[[120471,120471],"mapped",[110]],[[120472,120472],"mapped",[111]],[[120473,120473],"mapped",[112]],[[120474,120474],"mapped",[113]],[[120475,120475],"mapped",[114]],[[120476,120476],"mapped",[115]],[[120477,120477],"mapped",[116]],[[120478,120478],"mapped",[117]],[[120479,120479],"mapped",[118]],[[120480,120480],"mapped",[119]],[[120481,120481],"mapped",[120]],[[120482,120482],"mapped",[121]],[[120483,120483],"mapped",[122]],[[120484,120484],"mapped",[305]],[[120485,120485],"mapped",[567]],[[120486,120487],"disallowed"],[[120488,120488],"mapped",[945]],[[120489,120489],"mapped",[946]],[[120490,120490],"mapped",[947]],[[120491,120491],"mapped",[948]],[[120492,120492],"mapped",[949]],[[120493,120493],"mapped",[950]],[[120494,120494],"mapped",[951]],[[120495,120495],"mapped",[952]],[[120496,120496],"mapped",[953]],[[120497,120497],"mapped",[954]],[[120498,120498],"mapped",[955]],[[120499,120499],"mapped",[956]],[[120500,120500],"mapped",[957]],[[120501,120501],"mapped",[958]],[[120502,120502],"mapped",[959]],[[120503,120503],"mapped",[960]],[[120504,120504],"mapped",[961]],[[120505,120505],"mapped",[952]],[[120506,120506],"mapped",[963]],[[120507,120507],"mapped",[964]],[[120508,120508],"mapped",[965]],[[120509,120509],"mapped",[966]],[[120510,120510],"mapped",[967]],[[120511,120511],"mapped",[968]],[[120512,120512],"mapped",[969]],[[120513,120513],"mapped",[8711]],[[120514,120514],"mapped",[945]],[[120515,120515],"mapped",[946]],[[120516,120516],"mapped",[947]],[[120517,120517],"mapped",[948]],[[120518,120518],"mapped",[949]],[[120519,120519],"mapped",[950]],[[120520,120520],"mapped",[951]],[[120521,120521],"mapped",[952]],[[120522,120522],"mapped",[953]],[[120523,120523],"mapped",[954]],[[120524,120524],"mapped",[955]],[[120525,120525],"mapped",[956]],[[120526,120526],"mapped",[957]],[[120527,120527],"mapped",[958]],[[120528,120528],"mapped",[959]],[[120529,120529],"mapped",[960]],[[120530,120530],"mapped",[961]],[[120531,120532],"mapped",[963]],[[120533,120533],"mapped",[964]],[[120534,120534],"mapped",[965]],[[120535,120535],"mapped",[966]],[[120536,120536],"mapped",[967]],[[120537,120537],"mapped",[968]],[[120538,120538],"mapped",[969]],[[120539,120539],"mapped",[8706]],[[120540,120540],"mapped",[949]],[[120541,120541],"mapped",[952]],[[120542,120542],"mapped",[954]],[[120543,120543],"mapped",[966]],[[120544,120544],"mapped",[961]],[[120545,120545],"mapped",[960]],[[120546,120546],"mapped",[945]],[[120547,120547],"mapped",[946]],[[120548,120548],"mapped",[947]],[[120549,120549],"mapped",[948]],[[120550,120550],"mapped",[949]],[[120551,120551],"mapped",[950]],[[120552,120552],"mapped",[951]],[[120553,120553],"mapped",[952]],[[120554,120554],"mapped",[953]],[[120555,120555],"mapped",[954]],[[120556,120556],"mapped",[955]],[[120557,120557],"mapped",[956]],[[120558,120558],"mapped",[957]],[[120559,120559],"mapped",[958]],[[120560,120560],"mapped",[959]],[[120561,120561],"mapped",[960]],[[120562,120562],"mapped",[961]],[[120563,120563],"mapped",[952]],[[120564,120564],"mapped",[963]],[[120565,120565],"mapped",[964]],[[120566,120566],"mapped",[965]],[[120567,120567],"mapped",[966]],[[120568,120568],"mapped",[967]],[[120569,120569],"mapped",[968]],[[120570,120570],"mapped",[969]],[[120571,120571],"mapped",[8711]],[[120572,120572],"mapped",[945]],[[120573,120573],"mapped",[946]],[[120574,120574],"mapped",[947]],[[120575,120575],"mapped",[948]],[[120576,120576],"mapped",[949]],[[120577,120577],"mapped",[950]],[[120578,120578],"mapped",[951]],[[120579,120579],"mapped",[952]],[[120580,120580],"mapped",[953]],[[120581,120581],"mapped",[954]],[[120582,120582],"mapped",[955]],[[120583,120583],"mapped",[956]],[[120584,120584],"mapped",[957]],[[120585,120585],"mapped",[958]],[[120586,120586],"mapped",[959]],[[120587,120587],"mapped",[960]],[[120588,120588],"mapped",[961]],[[120589,120590],"mapped",[963]],[[120591,120591],"mapped",[964]],[[120592,120592],"mapped",[965]],[[120593,120593],"mapped",[966]],[[120594,120594],"mapped",[967]],[[120595,120595],"mapped",[968]],[[120596,120596],"mapped",[969]],[[120597,120597],"mapped",[8706]],[[120598,120598],"mapped",[949]],[[120599,120599],"mapped",[952]],[[120600,120600],"mapped",[954]],[[120601,120601],"mapped",[966]],[[120602,120602],"mapped",[961]],[[120603,120603],"mapped",[960]],[[120604,120604],"mapped",[945]],[[120605,120605],"mapped",[946]],[[120606,120606],"mapped",[947]],[[120607,120607],"mapped",[948]],[[120608,120608],"mapped",[949]],[[120609,120609],"mapped",[950]],[[120610,120610],"mapped",[951]],[[120611,120611],"mapped",[952]],[[120612,120612],"mapped",[953]],[[120613,120613],"mapped",[954]],[[120614,120614],"mapped",[955]],[[120615,120615],"mapped",[956]],[[120616,120616],"mapped",[957]],[[120617,120617],"mapped",[958]],[[120618,120618],"mapped",[959]],[[120619,120619],"mapped",[960]],[[120620,120620],"mapped",[961]],[[120621,120621],"mapped",[952]],[[120622,120622],"mapped",[963]],[[120623,120623],"mapped",[964]],[[120624,120624],"mapped",[965]],[[120625,120625],"mapped",[966]],[[120626,120626],"mapped",[967]],[[120627,120627],"mapped",[968]],[[120628,120628],"mapped",[969]],[[120629,120629],"mapped",[8711]],[[120630,120630],"mapped",[945]],[[120631,120631],"mapped",[946]],[[120632,120632],"mapped",[947]],[[120633,120633],"mapped",[948]],[[120634,120634],"mapped",[949]],[[120635,120635],"mapped",[950]],[[120636,120636],"mapped",[951]],[[120637,120637],"mapped",[952]],[[120638,120638],"mapped",[953]],[[120639,120639],"mapped",[954]],[[120640,120640],"mapped",[955]],[[120641,120641],"mapped",[956]],[[120642,120642],"mapped",[957]],[[120643,120643],"mapped",[958]],[[120644,120644],"mapped",[959]],[[120645,120645],"mapped",[960]],[[120646,120646],"mapped",[961]],[[120647,120648],"mapped",[963]],[[120649,120649],"mapped",[964]],[[120650,120650],"mapped",[965]],[[120651,120651],"mapped",[966]],[[120652,120652],"mapped",[967]],[[120653,120653],"mapped",[968]],[[120654,120654],"mapped",[969]],[[120655,120655],"mapped",[8706]],[[120656,120656],"mapped",[949]],[[120657,120657],"mapped",[952]],[[120658,120658],"mapped",[954]],[[120659,120659],"mapped",[966]],[[120660,120660],"mapped",[961]],[[120661,120661],"mapped",[960]],[[120662,120662],"mapped",[945]],[[120663,120663],"mapped",[946]],[[120664,120664],"mapped",[947]],[[120665,120665],"mapped",[948]],[[120666,120666],"mapped",[949]],[[120667,120667],"mapped",[950]],[[120668,120668],"mapped",[951]],[[120669,120669],"mapped",[952]],[[120670,120670],"mapped",[953]],[[120671,120671],"mapped",[954]],[[120672,120672],"mapped",[955]],[[120673,120673],"mapped",[956]],[[120674,120674],"mapped",[957]],[[120675,120675],"mapped",[958]],[[120676,120676],"mapped",[959]],[[120677,120677],"mapped",[960]],[[120678,120678],"mapped",[961]],[[120679,120679],"mapped",[952]],[[120680,120680],"mapped",[963]],[[120681,120681],"mapped",[964]],[[120682,120682],"mapped",[965]],[[120683,120683],"mapped",[966]],[[120684,120684],"mapped",[967]],[[120685,120685],"mapped",[968]],[[120686,120686],"mapped",[969]],[[120687,120687],"mapped",[8711]],[[120688,120688],"mapped",[945]],[[120689,120689],"mapped",[946]],[[120690,120690],"mapped",[947]],[[120691,120691],"mapped",[948]],[[120692,120692],"mapped",[949]],[[120693,120693],"mapped",[950]],[[120694,120694],"mapped",[951]],[[120695,120695],"mapped",[952]],[[120696,120696],"mapped",[953]],[[120697,120697],"mapped",[954]],[[120698,120698],"mapped",[955]],[[120699,120699],"mapped",[956]],[[120700,120700],"mapped",[957]],[[120701,120701],"mapped",[958]],[[120702,120702],"mapped",[959]],[[120703,120703],"mapped",[960]],[[120704,120704],"mapped",[961]],[[120705,120706],"mapped",[963]],[[120707,120707],"mapped",[964]],[[120708,120708],"mapped",[965]],[[120709,120709],"mapped",[966]],[[120710,120710],"mapped",[967]],[[120711,120711],"mapped",[968]],[[120712,120712],"mapped",[969]],[[120713,120713],"mapped",[8706]],[[120714,120714],"mapped",[949]],[[120715,120715],"mapped",[952]],[[120716,120716],"mapped",[954]],[[120717,120717],"mapped",[966]],[[120718,120718],"mapped",[961]],[[120719,120719],"mapped",[960]],[[120720,120720],"mapped",[945]],[[120721,120721],"mapped",[946]],[[120722,120722],"mapped",[947]],[[120723,120723],"mapped",[948]],[[120724,120724],"mapped",[949]],[[120725,120725],"mapped",[950]],[[120726,120726],"mapped",[951]],[[120727,120727],"mapped",[952]],[[120728,120728],"mapped",[953]],[[120729,120729],"mapped",[954]],[[120730,120730],"mapped",[955]],[[120731,120731],"mapped",[956]],[[120732,120732],"mapped",[957]],[[120733,120733],"mapped",[958]],[[120734,120734],"mapped",[959]],[[120735,120735],"mapped",[960]],[[120736,120736],"mapped",[961]],[[120737,120737],"mapped",[952]],[[120738,120738],"mapped",[963]],[[120739,120739],"mapped",[964]],[[120740,120740],"mapped",[965]],[[120741,120741],"mapped",[966]],[[120742,120742],"mapped",[967]],[[120743,120743],"mapped",[968]],[[120744,120744],"mapped",[969]],[[120745,120745],"mapped",[8711]],[[120746,120746],"mapped",[945]],[[120747,120747],"mapped",[946]],[[120748,120748],"mapped",[947]],[[120749,120749],"mapped",[948]],[[120750,120750],"mapped",[949]],[[120751,120751],"mapped",[950]],[[120752,120752],"mapped",[951]],[[120753,120753],"mapped",[952]],[[120754,120754],"mapped",[953]],[[120755,120755],"mapped",[954]],[[120756,120756],"mapped",[955]],[[120757,120757],"mapped",[956]],[[120758,120758],"mapped",[957]],[[120759,120759],"mapped",[958]],[[120760,120760],"mapped",[959]],[[120761,120761],"mapped",[960]],[[120762,120762],"mapped",[961]],[[120763,120764],"mapped",[963]],[[120765,120765],"mapped",[964]],[[120766,120766],"mapped",[965]],[[120767,120767],"mapped",[966]],[[120768,120768],"mapped",[967]],[[120769,120769],"mapped",[968]],[[120770,120770],"mapped",[969]],[[120771,120771],"mapped",[8706]],[[120772,120772],"mapped",[949]],[[120773,120773],"mapped",[952]],[[120774,120774],"mapped",[954]],[[120775,120775],"mapped",[966]],[[120776,120776],"mapped",[961]],[[120777,120777],"mapped",[960]],[[120778,120779],"mapped",[989]],[[120780,120781],"disallowed"],[[120782,120782],"mapped",[48]],[[120783,120783],"mapped",[49]],[[120784,120784],"mapped",[50]],[[120785,120785],"mapped",[51]],[[120786,120786],"mapped",[52]],[[120787,120787],"mapped",[53]],[[120788,120788],"mapped",[54]],[[120789,120789],"mapped",[55]],[[120790,120790],"mapped",[56]],[[120791,120791],"mapped",[57]],[[120792,120792],"mapped",[48]],[[120793,120793],"mapped",[49]],[[120794,120794],"mapped",[50]],[[120795,120795],"mapped",[51]],[[120796,120796],"mapped",[52]],[[120797,120797],"mapped",[53]],[[120798,120798],"mapped",[54]],[[120799,120799],"mapped",[55]],[[120800,120800],"mapped",[56]],[[120801,120801],"mapped",[57]],[[120802,120802],"mapped",[48]],[[120803,120803],"mapped",[49]],[[120804,120804],"mapped",[50]],[[120805,120805],"mapped",[51]],[[120806,120806],"mapped",[52]],[[120807,120807],"mapped",[53]],[[120808,120808],"mapped",[54]],[[120809,120809],"mapped",[55]],[[120810,120810],"mapped",[56]],[[120811,120811],"mapped",[57]],[[120812,120812],"mapped",[48]],[[120813,120813],"mapped",[49]],[[120814,120814],"mapped",[50]],[[120815,120815],"mapped",[51]],[[120816,120816],"mapped",[52]],[[120817,120817],"mapped",[53]],[[120818,120818],"mapped",[54]],[[120819,120819],"mapped",[55]],[[120820,120820],"mapped",[56]],[[120821,120821],"mapped",[57]],[[120822,120822],"mapped",[48]],[[120823,120823],"mapped",[49]],[[120824,120824],"mapped",[50]],[[120825,120825],"mapped",[51]],[[120826,120826],"mapped",[52]],[[120827,120827],"mapped",[53]],[[120828,120828],"mapped",[54]],[[120829,120829],"mapped",[55]],[[120830,120830],"mapped",[56]],[[120831,120831],"mapped",[57]],[[120832,121343],"valid",[],"NV8"],[[121344,121398],"valid"],[[121399,121402],"valid",[],"NV8"],[[121403,121452],"valid"],[[121453,121460],"valid",[],"NV8"],[[121461,121461],"valid"],[[121462,121475],"valid",[],"NV8"],[[121476,121476],"valid"],[[121477,121483],"valid",[],"NV8"],[[121484,121498],"disallowed"],[[121499,121503],"valid"],[[121504,121504],"disallowed"],[[121505,121519],"valid"],[[121520,124927],"disallowed"],[[124928,125124],"valid"],[[125125,125126],"disallowed"],[[125127,125135],"valid",[],"NV8"],[[125136,125142],"valid"],[[125143,126463],"disallowed"],[[126464,126464],"mapped",[1575]],[[126465,126465],"mapped",[1576]],[[126466,126466],"mapped",[1580]],[[126467,126467],"mapped",[1583]],[[126468,126468],"disallowed"],[[126469,126469],"mapped",[1608]],[[126470,126470],"mapped",[1586]],[[126471,126471],"mapped",[1581]],[[126472,126472],"mapped",[1591]],[[126473,126473],"mapped",[1610]],[[126474,126474],"mapped",[1603]],[[126475,126475],"mapped",[1604]],[[126476,126476],"mapped",[1605]],[[126477,126477],"mapped",[1606]],[[126478,126478],"mapped",[1587]],[[126479,126479],"mapped",[1593]],[[126480,126480],"mapped",[1601]],[[126481,126481],"mapped",[1589]],[[126482,126482],"mapped",[1602]],[[126483,126483],"mapped",[1585]],[[126484,126484],"mapped",[1588]],[[126485,126485],"mapped",[1578]],[[126486,126486],"mapped",[1579]],[[126487,126487],"mapped",[1582]],[[126488,126488],"mapped",[1584]],[[126489,126489],"mapped",[1590]],[[126490,126490],"mapped",[1592]],[[126491,126491],"mapped",[1594]],[[126492,126492],"mapped",[1646]],[[126493,126493],"mapped",[1722]],[[126494,126494],"mapped",[1697]],[[126495,126495],"mapped",[1647]],[[126496,126496],"disallowed"],[[126497,126497],"mapped",[1576]],[[126498,126498],"mapped",[1580]],[[126499,126499],"disallowed"],[[126500,126500],"mapped",[1607]],[[126501,126502],"disallowed"],[[126503,126503],"mapped",[1581]],[[126504,126504],"disallowed"],[[126505,126505],"mapped",[1610]],[[126506,126506],"mapped",[1603]],[[126507,126507],"mapped",[1604]],[[126508,126508],"mapped",[1605]],[[126509,126509],"mapped",[1606]],[[126510,126510],"mapped",[1587]],[[126511,126511],"mapped",[1593]],[[126512,126512],"mapped",[1601]],[[126513,126513],"mapped",[1589]],[[126514,126514],"mapped",[1602]],[[126515,126515],"disallowed"],[[126516,126516],"mapped",[1588]],[[126517,126517],"mapped",[1578]],[[126518,126518],"mapped",[1579]],[[126519,126519],"mapped",[1582]],[[126520,126520],"disallowed"],[[126521,126521],"mapped",[1590]],[[126522,126522],"disallowed"],[[126523,126523],"mapped",[1594]],[[126524,126529],"disallowed"],[[126530,126530],"mapped",[1580]],[[126531,126534],"disallowed"],[[126535,126535],"mapped",[1581]],[[126536,126536],"disallowed"],[[126537,126537],"mapped",[1610]],[[126538,126538],"disallowed"],[[126539,126539],"mapped",[1604]],[[126540,126540],"disallowed"],[[126541,126541],"mapped",[1606]],[[126542,126542],"mapped",[1587]],[[126543,126543],"mapped",[1593]],[[126544,126544],"disallowed"],[[126545,126545],"mapped",[1589]],[[126546,126546],"mapped",[1602]],[[126547,126547],"disallowed"],[[126548,126548],"mapped",[1588]],[[126549,126550],"disallowed"],[[126551,126551],"mapped",[1582]],[[126552,126552],"disallowed"],[[126553,126553],"mapped",[1590]],[[126554,126554],"disallowed"],[[126555,126555],"mapped",[1594]],[[126556,126556],"disallowed"],[[126557,126557],"mapped",[1722]],[[126558,126558],"disallowed"],[[126559,126559],"mapped",[1647]],[[126560,126560],"disallowed"],[[126561,126561],"mapped",[1576]],[[126562,126562],"mapped",[1580]],[[126563,126563],"disallowed"],[[126564,126564],"mapped",[1607]],[[126565,126566],"disallowed"],[[126567,126567],"mapped",[1581]],[[126568,126568],"mapped",[1591]],[[126569,126569],"mapped",[1610]],[[126570,126570],"mapped",[1603]],[[126571,126571],"disallowed"],[[126572,126572],"mapped",[1605]],[[126573,126573],"mapped",[1606]],[[126574,126574],"mapped",[1587]],[[126575,126575],"mapped",[1593]],[[126576,126576],"mapped",[1601]],[[126577,126577],"mapped",[1589]],[[126578,126578],"mapped",[1602]],[[126579,126579],"disallowed"],[[126580,126580],"mapped",[1588]],[[126581,126581],"mapped",[1578]],[[126582,126582],"mapped",[1579]],[[126583,126583],"mapped",[1582]],[[126584,126584],"disallowed"],[[126585,126585],"mapped",[1590]],[[126586,126586],"mapped",[1592]],[[126587,126587],"mapped",[1594]],[[126588,126588],"mapped",[1646]],[[126589,126589],"disallowed"],[[126590,126590],"mapped",[1697]],[[126591,126591],"disallowed"],[[126592,126592],"mapped",[1575]],[[126593,126593],"mapped",[1576]],[[126594,126594],"mapped",[1580]],[[126595,126595],"mapped",[1583]],[[126596,126596],"mapped",[1607]],[[126597,126597],"mapped",[1608]],[[126598,126598],"mapped",[1586]],[[126599,126599],"mapped",[1581]],[[126600,126600],"mapped",[1591]],[[126601,126601],"mapped",[1610]],[[126602,126602],"disallowed"],[[126603,126603],"mapped",[1604]],[[126604,126604],"mapped",[1605]],[[126605,126605],"mapped",[1606]],[[126606,126606],"mapped",[1587]],[[126607,126607],"mapped",[1593]],[[126608,126608],"mapped",[1601]],[[126609,126609],"mapped",[1589]],[[126610,126610],"mapped",[1602]],[[126611,126611],"mapped",[1585]],[[126612,126612],"mapped",[1588]],[[126613,126613],"mapped",[1578]],[[126614,126614],"mapped",[1579]],[[126615,126615],"mapped",[1582]],[[126616,126616],"mapped",[1584]],[[126617,126617],"mapped",[1590]],[[126618,126618],"mapped",[1592]],[[126619,126619],"mapped",[1594]],[[126620,126624],"disallowed"],[[126625,126625],"mapped",[1576]],[[126626,126626],"mapped",[1580]],[[126627,126627],"mapped",[1583]],[[126628,126628],"disallowed"],[[126629,126629],"mapped",[1608]],[[126630,126630],"mapped",[1586]],[[126631,126631],"mapped",[1581]],[[126632,126632],"mapped",[1591]],[[126633,126633],"mapped",[1610]],[[126634,126634],"disallowed"],[[126635,126635],"mapped",[1604]],[[126636,126636],"mapped",[1605]],[[126637,126637],"mapped",[1606]],[[126638,126638],"mapped",[1587]],[[126639,126639],"mapped",[1593]],[[126640,126640],"mapped",[1601]],[[126641,126641],"mapped",[1589]],[[126642,126642],"mapped",[1602]],[[126643,126643],"mapped",[1585]],[[126644,126644],"mapped",[1588]],[[126645,126645],"mapped",[1578]],[[126646,126646],"mapped",[1579]],[[126647,126647],"mapped",[1582]],[[126648,126648],"mapped",[1584]],[[126649,126649],"mapped",[1590]],[[126650,126650],"mapped",[1592]],[[126651,126651],"mapped",[1594]],[[126652,126703],"disallowed"],[[126704,126705],"valid",[],"NV8"],[[126706,126975],"disallowed"],[[126976,127019],"valid",[],"NV8"],[[127020,127023],"disallowed"],[[127024,127123],"valid",[],"NV8"],[[127124,127135],"disallowed"],[[127136,127150],"valid",[],"NV8"],[[127151,127152],"disallowed"],[[127153,127166],"valid",[],"NV8"],[[127167,127167],"valid",[],"NV8"],[[127168,127168],"disallowed"],[[127169,127183],"valid",[],"NV8"],[[127184,127184],"disallowed"],[[127185,127199],"valid",[],"NV8"],[[127200,127221],"valid",[],"NV8"],[[127222,127231],"disallowed"],[[127232,127232],"disallowed"],[[127233,127233],"disallowed_STD3_mapped",[48,44]],[[127234,127234],"disallowed_STD3_mapped",[49,44]],[[127235,127235],"disallowed_STD3_mapped",[50,44]],[[127236,127236],"disallowed_STD3_mapped",[51,44]],[[127237,127237],"disallowed_STD3_mapped",[52,44]],[[127238,127238],"disallowed_STD3_mapped",[53,44]],[[127239,127239],"disallowed_STD3_mapped",[54,44]],[[127240,127240],"disallowed_STD3_mapped",[55,44]],[[127241,127241],"disallowed_STD3_mapped",[56,44]],[[127242,127242],"disallowed_STD3_mapped",[57,44]],[[127243,127244],"valid",[],"NV8"],[[127245,127247],"disallowed"],[[127248,127248],"disallowed_STD3_mapped",[40,97,41]],[[127249,127249],"disallowed_STD3_mapped",[40,98,41]],[[127250,127250],"disallowed_STD3_mapped",[40,99,41]],[[127251,127251],"disallowed_STD3_mapped",[40,100,41]],[[127252,127252],"disallowed_STD3_mapped",[40,101,41]],[[127253,127253],"disallowed_STD3_mapped",[40,102,41]],[[127254,127254],"disallowed_STD3_mapped",[40,103,41]],[[127255,127255],"disallowed_STD3_mapped",[40,104,41]],[[127256,127256],"disallowed_STD3_mapped",[40,105,41]],[[127257,127257],"disallowed_STD3_mapped",[40,106,41]],[[127258,127258],"disallowed_STD3_mapped",[40,107,41]],[[127259,127259],"disallowed_STD3_mapped",[40,108,41]],[[127260,127260],"disallowed_STD3_mapped",[40,109,41]],[[127261,127261],"disallowed_STD3_mapped",[40,110,41]],[[127262,127262],"disallowed_STD3_mapped",[40,111,41]],[[127263,127263],"disallowed_STD3_mapped",[40,112,41]],[[127264,127264],"disallowed_STD3_mapped",[40,113,41]],[[127265,127265],"disallowed_STD3_mapped",[40,114,41]],[[127266,127266],"disallowed_STD3_mapped",[40,115,41]],[[127267,127267],"disallowed_STD3_mapped",[40,116,41]],[[127268,127268],"disallowed_STD3_mapped",[40,117,41]],[[127269,127269],"disallowed_STD3_mapped",[40,118,41]],[[127270,127270],"disallowed_STD3_mapped",[40,119,41]],[[127271,127271],"disallowed_STD3_mapped",[40,120,41]],[[127272,127272],"disallowed_STD3_mapped",[40,121,41]],[[127273,127273],"disallowed_STD3_mapped",[40,122,41]],[[127274,127274],"mapped",[12308,115,12309]],[[127275,127275],"mapped",[99]],[[127276,127276],"mapped",[114]],[[127277,127277],"mapped",[99,100]],[[127278,127278],"mapped",[119,122]],[[127279,127279],"disallowed"],[[127280,127280],"mapped",[97]],[[127281,127281],"mapped",[98]],[[127282,127282],"mapped",[99]],[[127283,127283],"mapped",[100]],[[127284,127284],"mapped",[101]],[[127285,127285],"mapped",[102]],[[127286,127286],"mapped",[103]],[[127287,127287],"mapped",[104]],[[127288,127288],"mapped",[105]],[[127289,127289],"mapped",[106]],[[127290,127290],"mapped",[107]],[[127291,127291],"mapped",[108]],[[127292,127292],"mapped",[109]],[[127293,127293],"mapped",[110]],[[127294,127294],"mapped",[111]],[[127295,127295],"mapped",[112]],[[127296,127296],"mapped",[113]],[[127297,127297],"mapped",[114]],[[127298,127298],"mapped",[115]],[[127299,127299],"mapped",[116]],[[127300,127300],"mapped",[117]],[[127301,127301],"mapped",[118]],[[127302,127302],"mapped",[119]],[[127303,127303],"mapped",[120]],[[127304,127304],"mapped",[121]],[[127305,127305],"mapped",[122]],[[127306,127306],"mapped",[104,118]],[[127307,127307],"mapped",[109,118]],[[127308,127308],"mapped",[115,100]],[[127309,127309],"mapped",[115,115]],[[127310,127310],"mapped",[112,112,118]],[[127311,127311],"mapped",[119,99]],[[127312,127318],"valid",[],"NV8"],[[127319,127319],"valid",[],"NV8"],[[127320,127326],"valid",[],"NV8"],[[127327,127327],"valid",[],"NV8"],[[127328,127337],"valid",[],"NV8"],[[127338,127338],"mapped",[109,99]],[[127339,127339],"mapped",[109,100]],[[127340,127343],"disallowed"],[[127344,127352],"valid",[],"NV8"],[[127353,127353],"valid",[],"NV8"],[[127354,127354],"valid",[],"NV8"],[[127355,127356],"valid",[],"NV8"],[[127357,127358],"valid",[],"NV8"],[[127359,127359],"valid",[],"NV8"],[[127360,127369],"valid",[],"NV8"],[[127370,127373],"valid",[],"NV8"],[[127374,127375],"valid",[],"NV8"],[[127376,127376],"mapped",[100,106]],[[127377,127386],"valid",[],"NV8"],[[127387,127461],"disallowed"],[[127462,127487],"valid",[],"NV8"],[[127488,127488],"mapped",[12411,12363]],[[127489,127489],"mapped",[12467,12467]],[[127490,127490],"mapped",[12469]],[[127491,127503],"disallowed"],[[127504,127504],"mapped",[25163]],[[127505,127505],"mapped",[23383]],[[127506,127506],"mapped",[21452]],[[127507,127507],"mapped",[12487]],[[127508,127508],"mapped",[20108]],[[127509,127509],"mapped",[22810]],[[127510,127510],"mapped",[35299]],[[127511,127511],"mapped",[22825]],[[127512,127512],"mapped",[20132]],[[127513,127513],"mapped",[26144]],[[127514,127514],"mapped",[28961]],[[127515,127515],"mapped",[26009]],[[127516,127516],"mapped",[21069]],[[127517,127517],"mapped",[24460]],[[127518,127518],"mapped",[20877]],[[127519,127519],"mapped",[26032]],[[127520,127520],"mapped",[21021]],[[127521,127521],"mapped",[32066]],[[127522,127522],"mapped",[29983]],[[127523,127523],"mapped",[36009]],[[127524,127524],"mapped",[22768]],[[127525,127525],"mapped",[21561]],[[127526,127526],"mapped",[28436]],[[127527,127527],"mapped",[25237]],[[127528,127528],"mapped",[25429]],[[127529,127529],"mapped",[19968]],[[127530,127530],"mapped",[19977]],[[127531,127531],"mapped",[36938]],[[127532,127532],"mapped",[24038]],[[127533,127533],"mapped",[20013]],[[127534,127534],"mapped",[21491]],[[127535,127535],"mapped",[25351]],[[127536,127536],"mapped",[36208]],[[127537,127537],"mapped",[25171]],[[127538,127538],"mapped",[31105]],[[127539,127539],"mapped",[31354]],[[127540,127540],"mapped",[21512]],[[127541,127541],"mapped",[28288]],[[127542,127542],"mapped",[26377]],[[127543,127543],"mapped",[26376]],[[127544,127544],"mapped",[30003]],[[127545,127545],"mapped",[21106]],[[127546,127546],"mapped",[21942]],[[127547,127551],"disallowed"],[[127552,127552],"mapped",[12308,26412,12309]],[[127553,127553],"mapped",[12308,19977,12309]],[[127554,127554],"mapped",[12308,20108,12309]],[[127555,127555],"mapped",[12308,23433,12309]],[[127556,127556],"mapped",[12308,28857,12309]],[[127557,127557],"mapped",[12308,25171,12309]],[[127558,127558],"mapped",[12308,30423,12309]],[[127559,127559],"mapped",[12308,21213,12309]],[[127560,127560],"mapped",[12308,25943,12309]],[[127561,127567],"disallowed"],[[127568,127568],"mapped",[24471]],[[127569,127569],"mapped",[21487]],[[127570,127743],"disallowed"],[[127744,127776],"valid",[],"NV8"],[[127777,127788],"valid",[],"NV8"],[[127789,127791],"valid",[],"NV8"],[[127792,127797],"valid",[],"NV8"],[[127798,127798],"valid",[],"NV8"],[[127799,127868],"valid",[],"NV8"],[[127869,127869],"valid",[],"NV8"],[[127870,127871],"valid",[],"NV8"],[[127872,127891],"valid",[],"NV8"],[[127892,127903],"valid",[],"NV8"],[[127904,127940],"valid",[],"NV8"],[[127941,127941],"valid",[],"NV8"],[[127942,127946],"valid",[],"NV8"],[[127947,127950],"valid",[],"NV8"],[[127951,127955],"valid",[],"NV8"],[[127956,127967],"valid",[],"NV8"],[[127968,127984],"valid",[],"NV8"],[[127985,127991],"valid",[],"NV8"],[[127992,127999],"valid",[],"NV8"],[[128000,128062],"valid",[],"NV8"],[[128063,128063],"valid",[],"NV8"],[[128064,128064],"valid",[],"NV8"],[[128065,128065],"valid",[],"NV8"],[[128066,128247],"valid",[],"NV8"],[[128248,128248],"valid",[],"NV8"],[[128249,128252],"valid",[],"NV8"],[[128253,128254],"valid",[],"NV8"],[[128255,128255],"valid",[],"NV8"],[[128256,128317],"valid",[],"NV8"],[[128318,128319],"valid",[],"NV8"],[[128320,128323],"valid",[],"NV8"],[[128324,128330],"valid",[],"NV8"],[[128331,128335],"valid",[],"NV8"],[[128336,128359],"valid",[],"NV8"],[[128360,128377],"valid",[],"NV8"],[[128378,128378],"disallowed"],[[128379,128419],"valid",[],"NV8"],[[128420,128420],"disallowed"],[[128421,128506],"valid",[],"NV8"],[[128507,128511],"valid",[],"NV8"],[[128512,128512],"valid",[],"NV8"],[[128513,128528],"valid",[],"NV8"],[[128529,128529],"valid",[],"NV8"],[[128530,128532],"valid",[],"NV8"],[[128533,128533],"valid",[],"NV8"],[[128534,128534],"valid",[],"NV8"],[[128535,128535],"valid",[],"NV8"],[[128536,128536],"valid",[],"NV8"],[[128537,128537],"valid",[],"NV8"],[[128538,128538],"valid",[],"NV8"],[[128539,128539],"valid",[],"NV8"],[[128540,128542],"valid",[],"NV8"],[[128543,128543],"valid",[],"NV8"],[[128544,128549],"valid",[],"NV8"],[[128550,128551],"valid",[],"NV8"],[[128552,128555],"valid",[],"NV8"],[[128556,128556],"valid",[],"NV8"],[[128557,128557],"valid",[],"NV8"],[[128558,128559],"valid",[],"NV8"],[[128560,128563],"valid",[],"NV8"],[[128564,128564],"valid",[],"NV8"],[[128565,128576],"valid",[],"NV8"],[[128577,128578],"valid",[],"NV8"],[[128579,128580],"valid",[],"NV8"],[[128581,128591],"valid",[],"NV8"],[[128592,128639],"valid",[],"NV8"],[[128640,128709],"valid",[],"NV8"],[[128710,128719],"valid",[],"NV8"],[[128720,128720],"valid",[],"NV8"],[[128721,128735],"disallowed"],[[128736,128748],"valid",[],"NV8"],[[128749,128751],"disallowed"],[[128752,128755],"valid",[],"NV8"],[[128756,128767],"disallowed"],[[128768,128883],"valid",[],"NV8"],[[128884,128895],"disallowed"],[[128896,128980],"valid",[],"NV8"],[[128981,129023],"disallowed"],[[129024,129035],"valid",[],"NV8"],[[129036,129039],"disallowed"],[[129040,129095],"valid",[],"NV8"],[[129096,129103],"disallowed"],[[129104,129113],"valid",[],"NV8"],[[129114,129119],"disallowed"],[[129120,129159],"valid",[],"NV8"],[[129160,129167],"disallowed"],[[129168,129197],"valid",[],"NV8"],[[129198,129295],"disallowed"],[[129296,129304],"valid",[],"NV8"],[[129305,129407],"disallowed"],[[129408,129412],"valid",[],"NV8"],[[129413,129471],"disallowed"],[[129472,129472],"valid",[],"NV8"],[[129473,131069],"disallowed"],[[131070,131071],"disallowed"],[[131072,173782],"valid"],[[173783,173823],"disallowed"],[[173824,177972],"valid"],[[177973,177983],"disallowed"],[[177984,178205],"valid"],[[178206,178207],"disallowed"],[[178208,183969],"valid"],[[183970,194559],"disallowed"],[[194560,194560],"mapped",[20029]],[[194561,194561],"mapped",[20024]],[[194562,194562],"mapped",[20033]],[[194563,194563],"mapped",[131362]],[[194564,194564],"mapped",[20320]],[[194565,194565],"mapped",[20398]],[[194566,194566],"mapped",[20411]],[[194567,194567],"mapped",[20482]],[[194568,194568],"mapped",[20602]],[[194569,194569],"mapped",[20633]],[[194570,194570],"mapped",[20711]],[[194571,194571],"mapped",[20687]],[[194572,194572],"mapped",[13470]],[[194573,194573],"mapped",[132666]],[[194574,194574],"mapped",[20813]],[[194575,194575],"mapped",[20820]],[[194576,194576],"mapped",[20836]],[[194577,194577],"mapped",[20855]],[[194578,194578],"mapped",[132380]],[[194579,194579],"mapped",[13497]],[[194580,194580],"mapped",[20839]],[[194581,194581],"mapped",[20877]],[[194582,194582],"mapped",[132427]],[[194583,194583],"mapped",[20887]],[[194584,194584],"mapped",[20900]],[[194585,194585],"mapped",[20172]],[[194586,194586],"mapped",[20908]],[[194587,194587],"mapped",[20917]],[[194588,194588],"mapped",[168415]],[[194589,194589],"mapped",[20981]],[[194590,194590],"mapped",[20995]],[[194591,194591],"mapped",[13535]],[[194592,194592],"mapped",[21051]],[[194593,194593],"mapped",[21062]],[[194594,194594],"mapped",[21106]],[[194595,194595],"mapped",[21111]],[[194596,194596],"mapped",[13589]],[[194597,194597],"mapped",[21191]],[[194598,194598],"mapped",[21193]],[[194599,194599],"mapped",[21220]],[[194600,194600],"mapped",[21242]],[[194601,194601],"mapped",[21253]],[[194602,194602],"mapped",[21254]],[[194603,194603],"mapped",[21271]],[[194604,194604],"mapped",[21321]],[[194605,194605],"mapped",[21329]],[[194606,194606],"mapped",[21338]],[[194607,194607],"mapped",[21363]],[[194608,194608],"mapped",[21373]],[[194609,194611],"mapped",[21375]],[[194612,194612],"mapped",[133676]],[[194613,194613],"mapped",[28784]],[[194614,194614],"mapped",[21450]],[[194615,194615],"mapped",[21471]],[[194616,194616],"mapped",[133987]],[[194617,194617],"mapped",[21483]],[[194618,194618],"mapped",[21489]],[[194619,194619],"mapped",[21510]],[[194620,194620],"mapped",[21662]],[[194621,194621],"mapped",[21560]],[[194622,194622],"mapped",[21576]],[[194623,194623],"mapped",[21608]],[[194624,194624],"mapped",[21666]],[[194625,194625],"mapped",[21750]],[[194626,194626],"mapped",[21776]],[[194627,194627],"mapped",[21843]],[[194628,194628],"mapped",[21859]],[[194629,194630],"mapped",[21892]],[[194631,194631],"mapped",[21913]],[[194632,194632],"mapped",[21931]],[[194633,194633],"mapped",[21939]],[[194634,194634],"mapped",[21954]],[[194635,194635],"mapped",[22294]],[[194636,194636],"mapped",[22022]],[[194637,194637],"mapped",[22295]],[[194638,194638],"mapped",[22097]],[[194639,194639],"mapped",[22132]],[[194640,194640],"mapped",[20999]],[[194641,194641],"mapped",[22766]],[[194642,194642],"mapped",[22478]],[[194643,194643],"mapped",[22516]],[[194644,194644],"mapped",[22541]],[[194645,194645],"mapped",[22411]],[[194646,194646],"mapped",[22578]],[[194647,194647],"mapped",[22577]],[[194648,194648],"mapped",[22700]],[[194649,194649],"mapped",[136420]],[[194650,194650],"mapped",[22770]],[[194651,194651],"mapped",[22775]],[[194652,194652],"mapped",[22790]],[[194653,194653],"mapped",[22810]],[[194654,194654],"mapped",[22818]],[[194655,194655],"mapped",[22882]],[[194656,194656],"mapped",[136872]],[[194657,194657],"mapped",[136938]],[[194658,194658],"mapped",[23020]],[[194659,194659],"mapped",[23067]],[[194660,194660],"mapped",[23079]],[[194661,194661],"mapped",[23000]],[[194662,194662],"mapped",[23142]],[[194663,194663],"mapped",[14062]],[[194664,194664],"disallowed"],[[194665,194665],"mapped",[23304]],[[194666,194667],"mapped",[23358]],[[194668,194668],"mapped",[137672]],[[194669,194669],"mapped",[23491]],[[194670,194670],"mapped",[23512]],[[194671,194671],"mapped",[23527]],[[194672,194672],"mapped",[23539]],[[194673,194673],"mapped",[138008]],[[194674,194674],"mapped",[23551]],[[194675,194675],"mapped",[23558]],[[194676,194676],"disallowed"],[[194677,194677],"mapped",[23586]],[[194678,194678],"mapped",[14209]],[[194679,194679],"mapped",[23648]],[[194680,194680],"mapped",[23662]],[[194681,194681],"mapped",[23744]],[[194682,194682],"mapped",[23693]],[[194683,194683],"mapped",[138724]],[[194684,194684],"mapped",[23875]],[[194685,194685],"mapped",[138726]],[[194686,194686],"mapped",[23918]],[[194687,194687],"mapped",[23915]],[[194688,194688],"mapped",[23932]],[[194689,194689],"mapped",[24033]],[[194690,194690],"mapped",[24034]],[[194691,194691],"mapped",[14383]],[[194692,194692],"mapped",[24061]],[[194693,194693],"mapped",[24104]],[[194694,194694],"mapped",[24125]],[[194695,194695],"mapped",[24169]],[[194696,194696],"mapped",[14434]],[[194697,194697],"mapped",[139651]],[[194698,194698],"mapped",[14460]],[[194699,194699],"mapped",[24240]],[[194700,194700],"mapped",[24243]],[[194701,194701],"mapped",[24246]],[[194702,194702],"mapped",[24266]],[[194703,194703],"mapped",[172946]],[[194704,194704],"mapped",[24318]],[[194705,194706],"mapped",[140081]],[[194707,194707],"mapped",[33281]],[[194708,194709],"mapped",[24354]],[[194710,194710],"mapped",[14535]],[[194711,194711],"mapped",[144056]],[[194712,194712],"mapped",[156122]],[[194713,194713],"mapped",[24418]],[[194714,194714],"mapped",[24427]],[[194715,194715],"mapped",[14563]],[[194716,194716],"mapped",[24474]],[[194717,194717],"mapped",[24525]],[[194718,194718],"mapped",[24535]],[[194719,194719],"mapped",[24569]],[[194720,194720],"mapped",[24705]],[[194721,194721],"mapped",[14650]],[[194722,194722],"mapped",[14620]],[[194723,194723],"mapped",[24724]],[[194724,194724],"mapped",[141012]],[[194725,194725],"mapped",[24775]],[[194726,194726],"mapped",[24904]],[[194727,194727],"mapped",[24908]],[[194728,194728],"mapped",[24910]],[[194729,194729],"mapped",[24908]],[[194730,194730],"mapped",[24954]],[[194731,194731],"mapped",[24974]],[[194732,194732],"mapped",[25010]],[[194733,194733],"mapped",[24996]],[[194734,194734],"mapped",[25007]],[[194735,194735],"mapped",[25054]],[[194736,194736],"mapped",[25074]],[[194737,194737],"mapped",[25078]],[[194738,194738],"mapped",[25104]],[[194739,194739],"mapped",[25115]],[[194740,194740],"mapped",[25181]],[[194741,194741],"mapped",[25265]],[[194742,194742],"mapped",[25300]],[[194743,194743],"mapped",[25424]],[[194744,194744],"mapped",[142092]],[[194745,194745],"mapped",[25405]],[[194746,194746],"mapped",[25340]],[[194747,194747],"mapped",[25448]],[[194748,194748],"mapped",[25475]],[[194749,194749],"mapped",[25572]],[[194750,194750],"mapped",[142321]],[[194751,194751],"mapped",[25634]],[[194752,194752],"mapped",[25541]],[[194753,194753],"mapped",[25513]],[[194754,194754],"mapped",[14894]],[[194755,194755],"mapped",[25705]],[[194756,194756],"mapped",[25726]],[[194757,194757],"mapped",[25757]],[[194758,194758],"mapped",[25719]],[[194759,194759],"mapped",[14956]],[[194760,194760],"mapped",[25935]],[[194761,194761],"mapped",[25964]],[[194762,194762],"mapped",[143370]],[[194763,194763],"mapped",[26083]],[[194764,194764],"mapped",[26360]],[[194765,194765],"mapped",[26185]],[[194766,194766],"mapped",[15129]],[[194767,194767],"mapped",[26257]],[[194768,194768],"mapped",[15112]],[[194769,194769],"mapped",[15076]],[[194770,194770],"mapped",[20882]],[[194771,194771],"mapped",[20885]],[[194772,194772],"mapped",[26368]],[[194773,194773],"mapped",[26268]],[[194774,194774],"mapped",[32941]],[[194775,194775],"mapped",[17369]],[[194776,194776],"mapped",[26391]],[[194777,194777],"mapped",[26395]],[[194778,194778],"mapped",[26401]],[[194779,194779],"mapped",[26462]],[[194780,194780],"mapped",[26451]],[[194781,194781],"mapped",[144323]],[[194782,194782],"mapped",[15177]],[[194783,194783],"mapped",[26618]],[[194784,194784],"mapped",[26501]],[[194785,194785],"mapped",[26706]],[[194786,194786],"mapped",[26757]],[[194787,194787],"mapped",[144493]],[[194788,194788],"mapped",[26766]],[[194789,194789],"mapped",[26655]],[[194790,194790],"mapped",[26900]],[[194791,194791],"mapped",[15261]],[[194792,194792],"mapped",[26946]],[[194793,194793],"mapped",[27043]],[[194794,194794],"mapped",[27114]],[[194795,194795],"mapped",[27304]],[[194796,194796],"mapped",[145059]],[[194797,194797],"mapped",[27355]],[[194798,194798],"mapped",[15384]],[[194799,194799],"mapped",[27425]],[[194800,194800],"mapped",[145575]],[[194801,194801],"mapped",[27476]],[[194802,194802],"mapped",[15438]],[[194803,194803],"mapped",[27506]],[[194804,194804],"mapped",[27551]],[[194805,194805],"mapped",[27578]],[[194806,194806],"mapped",[27579]],[[194807,194807],"mapped",[146061]],[[194808,194808],"mapped",[138507]],[[194809,194809],"mapped",[146170]],[[194810,194810],"mapped",[27726]],[[194811,194811],"mapped",[146620]],[[194812,194812],"mapped",[27839]],[[194813,194813],"mapped",[27853]],[[194814,194814],"mapped",[27751]],[[194815,194815],"mapped",[27926]],[[194816,194816],"mapped",[27966]],[[194817,194817],"mapped",[28023]],[[194818,194818],"mapped",[27969]],[[194819,194819],"mapped",[28009]],[[194820,194820],"mapped",[28024]],[[194821,194821],"mapped",[28037]],[[194822,194822],"mapped",[146718]],[[194823,194823],"mapped",[27956]],[[194824,194824],"mapped",[28207]],[[194825,194825],"mapped",[28270]],[[194826,194826],"mapped",[15667]],[[194827,194827],"mapped",[28363]],[[194828,194828],"mapped",[28359]],[[194829,194829],"mapped",[147153]],[[194830,194830],"mapped",[28153]],[[194831,194831],"mapped",[28526]],[[194832,194832],"mapped",[147294]],[[194833,194833],"mapped",[147342]],[[194834,194834],"mapped",[28614]],[[194835,194835],"mapped",[28729]],[[194836,194836],"mapped",[28702]],[[194837,194837],"mapped",[28699]],[[194838,194838],"mapped",[15766]],[[194839,194839],"mapped",[28746]],[[194840,194840],"mapped",[28797]],[[194841,194841],"mapped",[28791]],[[194842,194842],"mapped",[28845]],[[194843,194843],"mapped",[132389]],[[194844,194844],"mapped",[28997]],[[194845,194845],"mapped",[148067]],[[194846,194846],"mapped",[29084]],[[194847,194847],"disallowed"],[[194848,194848],"mapped",[29224]],[[194849,194849],"mapped",[29237]],[[194850,194850],"mapped",[29264]],[[194851,194851],"mapped",[149000]],[[194852,194852],"mapped",[29312]],[[194853,194853],"mapped",[29333]],[[194854,194854],"mapped",[149301]],[[194855,194855],"mapped",[149524]],[[194856,194856],"mapped",[29562]],[[194857,194857],"mapped",[29579]],[[194858,194858],"mapped",[16044]],[[194859,194859],"mapped",[29605]],[[194860,194861],"mapped",[16056]],[[194862,194862],"mapped",[29767]],[[194863,194863],"mapped",[29788]],[[194864,194864],"mapped",[29809]],[[194865,194865],"mapped",[29829]],[[194866,194866],"mapped",[29898]],[[194867,194867],"mapped",[16155]],[[194868,194868],"mapped",[29988]],[[194869,194869],"mapped",[150582]],[[194870,194870],"mapped",[30014]],[[194871,194871],"mapped",[150674]],[[194872,194872],"mapped",[30064]],[[194873,194873],"mapped",[139679]],[[194874,194874],"mapped",[30224]],[[194875,194875],"mapped",[151457]],[[194876,194876],"mapped",[151480]],[[194877,194877],"mapped",[151620]],[[194878,194878],"mapped",[16380]],[[194879,194879],"mapped",[16392]],[[194880,194880],"mapped",[30452]],[[194881,194881],"mapped",[151795]],[[194882,194882],"mapped",[151794]],[[194883,194883],"mapped",[151833]],[[194884,194884],"mapped",[151859]],[[194885,194885],"mapped",[30494]],[[194886,194887],"mapped",[30495]],[[194888,194888],"mapped",[30538]],[[194889,194889],"mapped",[16441]],[[194890,194890],"mapped",[30603]],[[194891,194891],"mapped",[16454]],[[194892,194892],"mapped",[16534]],[[194893,194893],"mapped",[152605]],[[194894,194894],"mapped",[30798]],[[194895,194895],"mapped",[30860]],[[194896,194896],"mapped",[30924]],[[194897,194897],"mapped",[16611]],[[194898,194898],"mapped",[153126]],[[194899,194899],"mapped",[31062]],[[194900,194900],"mapped",[153242]],[[194901,194901],"mapped",[153285]],[[194902,194902],"mapped",[31119]],[[194903,194903],"mapped",[31211]],[[194904,194904],"mapped",[16687]],[[194905,194905],"mapped",[31296]],[[194906,194906],"mapped",[31306]],[[194907,194907],"mapped",[31311]],[[194908,194908],"mapped",[153980]],[[194909,194910],"mapped",[154279]],[[194911,194911],"disallowed"],[[194912,194912],"mapped",[16898]],[[194913,194913],"mapped",[154539]],[[194914,194914],"mapped",[31686]],[[194915,194915],"mapped",[31689]],[[194916,194916],"mapped",[16935]],[[194917,194917],"mapped",[154752]],[[194918,194918],"mapped",[31954]],[[194919,194919],"mapped",[17056]],[[194920,194920],"mapped",[31976]],[[194921,194921],"mapped",[31971]],[[194922,194922],"mapped",[32000]],[[194923,194923],"mapped",[155526]],[[194924,194924],"mapped",[32099]],[[194925,194925],"mapped",[17153]],[[194926,194926],"mapped",[32199]],[[194927,194927],"mapped",[32258]],[[194928,194928],"mapped",[32325]],[[194929,194929],"mapped",[17204]],[[194930,194930],"mapped",[156200]],[[194931,194931],"mapped",[156231]],[[194932,194932],"mapped",[17241]],[[194933,194933],"mapped",[156377]],[[194934,194934],"mapped",[32634]],[[194935,194935],"mapped",[156478]],[[194936,194936],"mapped",[32661]],[[194937,194937],"mapped",[32762]],[[194938,194938],"mapped",[32773]],[[194939,194939],"mapped",[156890]],[[194940,194940],"mapped",[156963]],[[194941,194941],"mapped",[32864]],[[194942,194942],"mapped",[157096]],[[194943,194943],"mapped",[32880]],[[194944,194944],"mapped",[144223]],[[194945,194945],"mapped",[17365]],[[194946,194946],"mapped",[32946]],[[194947,194947],"mapped",[33027]],[[194948,194948],"mapped",[17419]],[[194949,194949],"mapped",[33086]],[[194950,194950],"mapped",[23221]],[[194951,194951],"mapped",[157607]],[[194952,194952],"mapped",[157621]],[[194953,194953],"mapped",[144275]],[[194954,194954],"mapped",[144284]],[[194955,194955],"mapped",[33281]],[[194956,194956],"mapped",[33284]],[[194957,194957],"mapped",[36766]],[[194958,194958],"mapped",[17515]],[[194959,194959],"mapped",[33425]],[[194960,194960],"mapped",[33419]],[[194961,194961],"mapped",[33437]],[[194962,194962],"mapped",[21171]],[[194963,194963],"mapped",[33457]],[[194964,194964],"mapped",[33459]],[[194965,194965],"mapped",[33469]],[[194966,194966],"mapped",[33510]],[[194967,194967],"mapped",[158524]],[[194968,194968],"mapped",[33509]],[[194969,194969],"mapped",[33565]],[[194970,194970],"mapped",[33635]],[[194971,194971],"mapped",[33709]],[[194972,194972],"mapped",[33571]],[[194973,194973],"mapped",[33725]],[[194974,194974],"mapped",[33767]],[[194975,194975],"mapped",[33879]],[[194976,194976],"mapped",[33619]],[[194977,194977],"mapped",[33738]],[[194978,194978],"mapped",[33740]],[[194979,194979],"mapped",[33756]],[[194980,194980],"mapped",[158774]],[[194981,194981],"mapped",[159083]],[[194982,194982],"mapped",[158933]],[[194983,194983],"mapped",[17707]],[[194984,194984],"mapped",[34033]],[[194985,194985],"mapped",[34035]],[[194986,194986],"mapped",[34070]],[[194987,194987],"mapped",[160714]],[[194988,194988],"mapped",[34148]],[[194989,194989],"mapped",[159532]],[[194990,194990],"mapped",[17757]],[[194991,194991],"mapped",[17761]],[[194992,194992],"mapped",[159665]],[[194993,194993],"mapped",[159954]],[[194994,194994],"mapped",[17771]],[[194995,194995],"mapped",[34384]],[[194996,194996],"mapped",[34396]],[[194997,194997],"mapped",[34407]],[[194998,194998],"mapped",[34409]],[[194999,194999],"mapped",[34473]],[[195000,195000],"mapped",[34440]],[[195001,195001],"mapped",[34574]],[[195002,195002],"mapped",[34530]],[[195003,195003],"mapped",[34681]],[[195004,195004],"mapped",[34600]],[[195005,195005],"mapped",[34667]],[[195006,195006],"mapped",[34694]],[[195007,195007],"disallowed"],[[195008,195008],"mapped",[34785]],[[195009,195009],"mapped",[34817]],[[195010,195010],"mapped",[17913]],[[195011,195011],"mapped",[34912]],[[195012,195012],"mapped",[34915]],[[195013,195013],"mapped",[161383]],[[195014,195014],"mapped",[35031]],[[195015,195015],"mapped",[35038]],[[195016,195016],"mapped",[17973]],[[195017,195017],"mapped",[35066]],[[195018,195018],"mapped",[13499]],[[195019,195019],"mapped",[161966]],[[195020,195020],"mapped",[162150]],[[195021,195021],"mapped",[18110]],[[195022,195022],"mapped",[18119]],[[195023,195023],"mapped",[35488]],[[195024,195024],"mapped",[35565]],[[195025,195025],"mapped",[35722]],[[195026,195026],"mapped",[35925]],[[195027,195027],"mapped",[162984]],[[195028,195028],"mapped",[36011]],[[195029,195029],"mapped",[36033]],[[195030,195030],"mapped",[36123]],[[195031,195031],"mapped",[36215]],[[195032,195032],"mapped",[163631]],[[195033,195033],"mapped",[133124]],[[195034,195034],"mapped",[36299]],[[195035,195035],"mapped",[36284]],[[195036,195036],"mapped",[36336]],[[195037,195037],"mapped",[133342]],[[195038,195038],"mapped",[36564]],[[195039,195039],"mapped",[36664]],[[195040,195040],"mapped",[165330]],[[195041,195041],"mapped",[165357]],[[195042,195042],"mapped",[37012]],[[195043,195043],"mapped",[37105]],[[195044,195044],"mapped",[37137]],[[195045,195045],"mapped",[165678]],[[195046,195046],"mapped",[37147]],[[195047,195047],"mapped",[37432]],[[195048,195048],"mapped",[37591]],[[195049,195049],"mapped",[37592]],[[195050,195050],"mapped",[37500]],[[195051,195051],"mapped",[37881]],[[195052,195052],"mapped",[37909]],[[195053,195053],"mapped",[166906]],[[195054,195054],"mapped",[38283]],[[195055,195055],"mapped",[18837]],[[195056,195056],"mapped",[38327]],[[195057,195057],"mapped",[167287]],[[195058,195058],"mapped",[18918]],[[195059,195059],"mapped",[38595]],[[195060,195060],"mapped",[23986]],[[195061,195061],"mapped",[38691]],[[195062,195062],"mapped",[168261]],[[195063,195063],"mapped",[168474]],[[195064,195064],"mapped",[19054]],[[195065,195065],"mapped",[19062]],[[195066,195066],"mapped",[38880]],[[195067,195067],"mapped",[168970]],[[195068,195068],"mapped",[19122]],[[195069,195069],"mapped",[169110]],[[195070,195071],"mapped",[38923]],[[195072,195072],"mapped",[38953]],[[195073,195073],"mapped",[169398]],[[195074,195074],"mapped",[39138]],[[195075,195075],"mapped",[19251]],[[195076,195076],"mapped",[39209]],[[195077,195077],"mapped",[39335]],[[195078,195078],"mapped",[39362]],[[195079,195079],"mapped",[39422]],[[195080,195080],"mapped",[19406]],[[195081,195081],"mapped",[170800]],[[195082,195082],"mapped",[39698]],[[195083,195083],"mapped",[40000]],[[195084,195084],"mapped",[40189]],[[195085,195085],"mapped",[19662]],[[195086,195086],"mapped",[19693]],[[195087,195087],"mapped",[40295]],[[195088,195088],"mapped",[172238]],[[195089,195089],"mapped",[19704]],[[195090,195090],"mapped",[172293]],[[195091,195091],"mapped",[172558]],[[195092,195092],"mapped",[172689]],[[195093,195093],"mapped",[40635]],[[195094,195094],"mapped",[19798]],[[195095,195095],"mapped",[40697]],[[195096,195096],"mapped",[40702]],[[195097,195097],"mapped",[40709]],[[195098,195098],"mapped",[40719]],[[195099,195099],"mapped",[40726]],[[195100,195100],"mapped",[40763]],[[195101,195101],"mapped",[173568]],[[195102,196605],"disallowed"],[[196606,196607],"disallowed"],[[196608,262141],"disallowed"],[[262142,262143],"disallowed"],[[262144,327677],"disallowed"],[[327678,327679],"disallowed"],[[327680,393213],"disallowed"],[[393214,393215],"disallowed"],[[393216,458749],"disallowed"],[[458750,458751],"disallowed"],[[458752,524285],"disallowed"],[[524286,524287],"disallowed"],[[524288,589821],"disallowed"],[[589822,589823],"disallowed"],[[589824,655357],"disallowed"],[[655358,655359],"disallowed"],[[655360,720893],"disallowed"],[[720894,720895],"disallowed"],[[720896,786429],"disallowed"],[[786430,786431],"disallowed"],[[786432,851965],"disallowed"],[[851966,851967],"disallowed"],[[851968,917501],"disallowed"],[[917502,917503],"disallowed"],[[917504,917504],"disallowed"],[[917505,917505],"disallowed"],[[917506,917535],"disallowed"],[[917536,917631],"disallowed"],[[917632,917759],"disallowed"],[[917760,917999],"ignored"],[[918000,983037],"disallowed"],[[983038,983039],"disallowed"],[[983040,1048573],"disallowed"],[[1048574,1048575],"disallowed"],[[1048576,1114109],"disallowed"],[[1114110,1114111],"disallowed"]]'); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __nccwpck_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ var threw = true; -/******/ try { -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete __webpack_module_cache__[moduleId]; -/******/ } -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(566); -/******/ module.exports = __webpack_exports__; -/******/ -/******/ })() -; diff --git a/.github/scripts/buildActions.sh b/.github/scripts/buildActions.sh index fa29c023ce518..ad2156efd6fc6 100755 --- a/.github/scripts/buildActions.sh +++ b/.github/scripts/buildActions.sh @@ -15,7 +15,7 @@ declare -r GITHUB_ACTIONS=( "$ACTIONS_DIR/checkAndroidStatus/checkAndroidStatus.ts" "$ACTIONS_DIR/checkDeployBlockers/checkDeployBlockers.ts" "$ACTIONS_DIR/checkSVGCompression/checkSVGCompression.ts" - "$ACTIONS_DIR/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts" + "$ACTIONS_DIR/failureNotifier/failureNotifier.ts" "$ACTIONS_DIR/formatCodeCovComment/formatCodeCovComment.ts" "$ACTIONS_DIR/getAndroidRolloutPercentage/getAndroidRolloutPercentage.ts" diff --git a/.github/workflows/createDeployChecklist.yml b/.github/workflows/createDeployChecklist.yml index 7571b5fc271ba..742d8a40879e4 100644 --- a/.github/workflows/createDeployChecklist.yml +++ b/.github/workflows/createDeployChecklist.yml @@ -16,6 +16,6 @@ jobs: uses: ./.github/actions/composite/setupNode - name: Create or update deploy checklist - uses: ./.github/actions/javascript/createOrUpdateStagingDeploy - with: + run: npx ts-node scripts/createOrUpdateStagingDeploy.ts + env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} From 7516d47f527baeb00cbcd57192da861f50adb98a Mon Sep 17 00:00:00 2001 From: rory Date: Fri, 20 Feb 2026 17:26:28 -0800 Subject: [PATCH 4/8] Unify isVerified/isResolved into isChecked with a single ChecklistItem type Both StagingDeployCashPR and StagingDeployCashBlocker had the same shape with differently-named boolean keys. Collapsing them into ChecklistItem with isChecked removes the generic K parameter from parseChecklistSection and preserveCheckboxState, making both functions simpler to read and call. Co-authored-by: Cursor --- .github/libs/StagingDeployUtils.ts | 49 +++++++------------ scripts/createOrUpdateStagingDeploy.ts | 33 +++++-------- tests/unit/StagingDeployUtilsTest.ts | 14 +++--- tests/unit/createOrUpdateStagingDeployTest.ts | 12 ++--- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/.github/libs/StagingDeployUtils.ts b/.github/libs/StagingDeployUtils.ts index 1e34ce971e3af..9ffee036a2678 100644 --- a/.github/libs/StagingDeployUtils.ts +++ b/.github/libs/StagingDeployUtils.ts @@ -8,16 +8,10 @@ import {isEmptyObject} from './isEmptyObject'; type OctokitIssueItem = OctokitComponents['schemas']['issue']; -type StagingDeployCashPR = { +type ChecklistItem = { url: string; number: number; - isVerified: boolean; -}; - -type StagingDeployCashBlocker = { - url: string; - number: number; - isResolved: boolean; + isChecked: boolean; }; type StagingDeployCashBody = { @@ -45,10 +39,10 @@ type StagingDeployCashData = { url: string; number: number; labels: OctokitIssueItem['labels']; - PRList: StagingDeployCashPR[]; - PRListMobileExpensify: StagingDeployCashPR[]; - deployBlockers: StagingDeployCashBlocker[]; - internalQAPRList: StagingDeployCashBlocker[]; + PRList: ChecklistItem[]; + PRListMobileExpensify: ChecklistItem[]; + deployBlockers: ChecklistItem[]; + internalQAPRList: ChecklistItem[]; isSentryChecked: boolean; isGHStatusChecked: boolean; version: string; @@ -57,15 +51,9 @@ type StagingDeployCashData = { /** * Generic checklist section parser. Extracts a section from the issue body, - * parses checkbox items within it, and returns typed objects sorted by number. + * parses checkbox items within it, and returns ChecklistItems sorted by number. */ -function parseChecklistSection( - issueBody: string | null | undefined, - sectionRegex: RegExp, - itemRegex: RegExp, - checkedKey: K, - urlTransform?: (url: string) => string, -): Array<{url: string; number: number} & Record> { +function parseChecklistSection(issueBody: string | null | undefined, sectionRegex: RegExp, itemRegex: RegExp, urlTransform?: (url: string) => string): ChecklistItem[] { const sectionMatch = issueBody?.match(sectionRegex) ?? null; if (sectionMatch?.length !== 2) { return []; @@ -75,34 +63,33 @@ function parseChecklistSection( return { url: urlTransform ? urlTransform(rawUrl) : rawUrl, number: Number.parseInt(match[3], 10), - [checkedKey]: match[1] === 'x', - } as {url: string; number: number} & Record; + isChecked: match[1] === 'x', + }; }); return items.sort((a, b) => a.number - b.number); } -function getStagingDeployCashPRList(issue: OctokitIssueItem): StagingDeployCashPR[] { - const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); +function getStagingDeployCashPRList(issue: OctokitIssueItem): ChecklistItem[] { + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST.PULL_REQUEST_REGEX.source})`, 'g')); if (result.length === 0) { console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); } return result; } -function getStagingDeployCashPRListMobileExpensify(issue: OctokitIssueItem): StagingDeployCashPR[] { - return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); +function getStagingDeployCashPRListMobileExpensify(issue: OctokitIssueItem): ChecklistItem[] { + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } -function getStagingDeployCashDeployBlockers(issue: OctokitIssueItem): StagingDeployCashBlocker[] { - return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); +function getStagingDeployCashDeployBlockers(issue: OctokitIssueItem): ChecklistItem[] { + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } -function getStagingDeployCashInternalQA(issue: OctokitIssueItem): StagingDeployCashBlocker[] { +function getStagingDeployCashInternalQA(issue: OctokitIssueItem): ChecklistItem[] { return parseChecklistSection( issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.PULL_REQUEST_REGEX.source})`, 'g'), - 'isResolved', (url) => url.split('-').at(0)?.trim() ?? '', ); } @@ -271,4 +258,4 @@ async function generateStagingDeployCashBodyAndAssignees({ } export {getStagingDeployCash, getStagingDeployCashData, generateStagingDeployCashBodyAndAssignees, parseChecklistSection}; -export type {StagingDeployCashPR, StagingDeployCashBlocker, StagingDeployCashBody, StagingDeployCashParams, StagingDeployCashData}; +export type {ChecklistItem, StagingDeployCashBody, StagingDeployCashParams, StagingDeployCashData}; diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateStagingDeploy.ts index 42a425cc0cf7b..17eb43b929f43 100644 --- a/scripts/createOrUpdateStagingDeploy.ts +++ b/scripts/createOrUpdateStagingDeploy.ts @@ -8,7 +8,7 @@ import GithubUtils from '@github/libs/GithubUtils'; import GitUtils from '@github/libs/GitUtils'; import type {MergedPR, SubmoduleUpdate} from '@github/libs/GitUtils'; import {generateStagingDeployCashBodyAndAssignees, getStagingDeployCashData} from '@github/libs/StagingDeployUtils'; -import type {StagingDeployCashBlocker, StagingDeployCashData, StagingDeployCashPR} from '@github/libs/StagingDeployUtils'; +import type {ChecklistItem, StagingDeployCashData} from '@github/libs/StagingDeployUtils'; type IssuesCreateResponse = Awaited>['data']; @@ -19,16 +19,12 @@ type PackageJson = { type TimelineEntry = {type: 'pr'; prNumber: number; date: string} | {type: 'submodule'; version: string; date: string; commit: string}; /** - * Preserve the checked state (isVerified/isResolved) from a previous checklist when building a new one. + * Preserve the checked state from a previous checklist when building a new one. */ -function preserveCheckboxState( - items: Array<{number: number; url: string}>, - currentList: T[] | undefined, - checkedKey: 'isVerified' | 'isResolved', -): T[] { +function preserveCheckboxState(items: Array<{number: number; url: string}>, currentList: ChecklistItem[] | undefined): ChecklistItem[] { return items.map((item) => { const existing = currentList?.find((c) => c.number === item.number); - return {...item, [checkedKey]: existing ? (existing as Record)[checkedKey] : false} as T; + return {...item, isChecked: existing?.isChecked ?? false}; }); } @@ -154,22 +150,20 @@ async function buildUpdateChecklistParams({ currentChecklistData: StagingDeployCashData; chronologicalSection: string; }) { - const PRList = preserveCheckboxState( + const PRList = preserveCheckboxState( newPRNumbers.map((prNum) => ({ number: prNum, url: GithubUtils.getPullRequestURLFromNumber(prNum, CONST.APP_REPO_URL), })), currentChecklistData.PRList, - 'isVerified', ); - const PRListMobileExpensify = preserveCheckboxState( + const PRListMobileExpensify = preserveCheckboxState( mergedMobileExpensifyPREntries.map((entry) => ({ number: entry.prNumber, url: GithubUtils.getPullRequestURLFromNumber(entry.prNumber, CONST.MOBILE_EXPENSIFY_URL), })), currentChecklistData.PRListMobileExpensify, - 'isVerified', ); // Include existing Mobile-Expensify PRs from the current checklist that aren't in the new merged list @@ -186,16 +180,15 @@ async function buildUpdateChecklistParams({ labels: CONST.LABELS.DEPLOY_BLOCKER, }); - const deployBlockers = preserveCheckboxState( + const deployBlockers = preserveCheckboxState( openDeployBlockers.map((db) => ({number: db.number, url: db.html_url})), currentChecklistData.deployBlockers, - 'isResolved', ); // Add demoted/closed blockers from current checklist and auto-check them for (const deployBlocker of currentChecklistData.deployBlockers) { - const isResolved = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); - deployBlockers.push({...deployBlocker, isResolved}); + const isChecked = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); + deployBlockers.push({...deployBlocker, isChecked}); } const didVersionChange = newVersion !== currentChecklistData.version; @@ -204,11 +197,11 @@ async function buildUpdateChecklistParams({ tag: newVersion, PRList: PRList.map((pr) => pr.number), PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.number), - verifiedPRList: PRList.filter((pr) => pr.isVerified).map((pr) => pr.number), - verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isVerified).map((pr) => pr.number), + verifiedPRList: PRList.filter((pr) => pr.isChecked).map((pr) => pr.number), + verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isChecked).map((pr) => pr.number), deployBlockers: deployBlockers.map((blocker) => blocker.url), - resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isResolved).map((blocker) => blocker.url), - resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isResolved).map((pr) => pr.number), + resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isChecked).map((blocker) => blocker.url), + resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isChecked).map((pr) => pr.number), isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, previousTag: previousChecklistData.version, diff --git a/tests/unit/StagingDeployUtilsTest.ts b/tests/unit/StagingDeployUtilsTest.ts index 4fa7b8800df85..44e0686d7da0a 100644 --- a/tests/unit/StagingDeployUtilsTest.ts +++ b/tests/unit/StagingDeployUtilsTest.ts @@ -14,7 +14,7 @@ const mockListIssues = jest.fn(); type Label = { id: number; number?: number; - isVerified?: boolean; + isChecked?: boolean; node_id: string; url: string; name: string; @@ -95,17 +95,17 @@ describe('StagingDeployUtils', () => { { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21`, number: 21, - isVerified: false, + isChecked: false, }, { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/22`, number: 22, - isVerified: true, + isChecked: true, }, { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/23`, number: 23, - isVerified: false, + isChecked: false, }, ], PRListMobileExpensify: [], @@ -136,17 +136,17 @@ describe('StagingDeployUtils', () => { { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1`, number: 1, - isResolved: false, + isChecked: false, }, { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2`, number: 2, - isResolved: true, + isChecked: true, }, { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234`, number: 1234, - isResolved: false, + isChecked: false, }, ]; diff --git a/tests/unit/createOrUpdateStagingDeployTest.ts b/tests/unit/createOrUpdateStagingDeployTest.ts index d3dea3f03fde5..9d63caba9f5e4 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.ts +++ b/tests/unit/createOrUpdateStagingDeployTest.ts @@ -621,12 +621,12 @@ describe('createOrUpdateStagingDeployCash', () => { number: 29, labels: [LABELS.STAGING_DEPLOY_CASH], PRList: [ - {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, number: 6, isVerified: true}, - {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/8`, number: 8, isVerified: true}, + {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, number: 6, isChecked: true}, + {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/8`, number: 8, isChecked: true}, ], PRListMobileExpensify: [ - {url: `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/20`, number: 20, isVerified: true}, - {url: `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/22`, number: 22, isVerified: true}, + {url: `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/20`, number: 20, isChecked: true}, + {url: `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/22`, number: 22, isChecked: true}, ], deployBlockers: [], internalQAPRList: [], @@ -690,8 +690,8 @@ describe('createOrUpdateStagingDeployCash', () => { number: 29, labels: [LABELS.STAGING_DEPLOY_CASH], PRList: [ - {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, number: 6, isVerified: true}, - {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/8`, number: 8, isVerified: true}, + {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/6`, number: 6, isChecked: true}, + {url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/8`, number: 8, isChecked: true}, ], PRListMobileExpensify: [], // No Mobile-Expensify PRs in previous checklist deployBlockers: [], From 93f2f320292c188b4eae8f777e9355dcf8b79c30 Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 21 Feb 2026 12:51:14 -0800 Subject: [PATCH 5/8] Rebuild GitHub Actions bundles after merge Co-authored-by: Cursor --- .../javascript/isStagingDeployLocked/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isStagingDeployLocked/index.js index bb4049bac1a82..7b8a14117bf5b 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isStagingDeployLocked/index.js @@ -12097,9 +12097,9 @@ const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); const isEmptyObject_1 = __nccwpck_require__(6497); /** * Generic checklist section parser. Extracts a section from the issue body, - * parses checkbox items within it, and returns typed objects sorted by number. + * parses checkbox items within it, and returns ChecklistItems sorted by number. */ -function parseChecklistSection(issueBody, sectionRegex, itemRegex, checkedKey, urlTransform) { +function parseChecklistSection(issueBody, sectionRegex, itemRegex, urlTransform) { const sectionMatch = issueBody?.match(sectionRegex) ?? null; if (sectionMatch?.length !== 2) { return []; @@ -12109,26 +12109,26 @@ function parseChecklistSection(issueBody, sectionRegex, itemRegex, checkedKey, u return { url: urlTransform ? urlTransform(rawUrl) : rawUrl, number: Number.parseInt(match[3], 10), - [checkedKey]: match[1] === 'x', + isChecked: match[1] === 'x', }; }); return items.sort((a, b) => a.number - b.number); } function getStagingDeployCashPRList(issue) { - const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g')); if (result.length === 0) { console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); } return result; } function getStagingDeployCashPRListMobileExpensify(issue) { - return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isVerified'); + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } function getStagingDeployCashDeployBlockers(issue) { - return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved'); + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } function getStagingDeployCashInternalQA(issue) { - return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), 'isResolved', (url) => url.split('-').at(0)?.trim() ?? ''); + return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), (url) => url.split('-').at(0)?.trim() ?? ''); } async function getStagingDeployCash() { const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ From 59fba25c223b951e85060013eaf3a062b82378ad Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 21 Feb 2026 13:02:54 -0800 Subject: [PATCH 6/8] Restore useful comments in createOrUpdateStagingDeploy Add back comments that explain non-obvious logic (submodule grouping algorithm, version naming conventions) and section markers that help orient readers in the long run() function. Co-authored-by: Cursor --- scripts/createOrUpdateStagingDeploy.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateStagingDeploy.ts index 17eb43b929f43..87c621d99b577 100644 --- a/scripts/createOrUpdateStagingDeploy.ts +++ b/scripts/createOrUpdateStagingDeploy.ts @@ -41,6 +41,7 @@ async function buildChronologicalSection({ return ''; } + // Look up workflow run URLs for each submodule update commit const submoduleRunURLs = new Map(); const results = await Promise.allSettled( submoduleUpdates.map(async (update) => { @@ -54,6 +55,9 @@ async function buildChronologicalSection({ } } + // Group Mobile-Expensify PRs by the submodule update that introduced them. + // A Mobile-Expensify PR is assigned to the first submodule bump whose date >= PR merge date, + // because merging to Mobile-Expensify doesn't matter until the submodule is actually bumped in App. const sortedSubmoduleUpdates = [...submoduleUpdates].sort((a, b) => a.date.localeCompare(b.date)); const mobileExpensifyPRsBySubmodule = new Map(); const mobileExpensifyPRsPendingSubmoduleUpdate: MergedPR[] = []; @@ -68,6 +72,7 @@ async function buildChronologicalSection({ } } + // Merge PRs and submodule updates into a single chronological timeline const timeline: TimelineEntry[] = [ ...chronologicalPREntries.map((pr): TimelineEntry => ({type: 'pr', prNumber: pr.prNumber, date: pr.date})), ...submoduleUpdates.map((update): TimelineEntry => ({type: 'submodule', version: update.version, date: update.date, commit: update.commit})), @@ -133,7 +138,7 @@ function buildNewChecklistParams({ } /** - * Build params for updating an existing open deploy checklist, preserving verified/resolved state. + * Build params for updating an existing open deploy checklist, preserving checked state. */ async function buildUpdateChecklistParams({ newVersion, @@ -212,7 +217,9 @@ async function buildUpdateChecklistParams({ async function run(): Promise { // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')) as PackageJson; + // e.g. '1.2.3-4' const newVersion = packageJson.version; + // e.g. '1.2.3-4-staging' const newStagingTag = `${packageJson.version}-staging`; try { @@ -224,6 +231,7 @@ async function run(): Promise { state: 'all', }); + // If the most recent checklist is open we'll update it; otherwise we create a new one const mostRecentChecklist = recentDeployChecklists.at(0); if (!mostRecentChecklist) { throw new Error('Could not find the most recent checklist'); @@ -241,9 +249,11 @@ async function run(): Promise { throw new Error('Could not find the previous checklist'); } + // Parse checklist data from the previous (and optionally current) deploy checklists const previousChecklistData = getStagingDeployCashData(previousChecklist); const currentChecklistData: StagingDeployCashData | undefined = shouldCreateNewDeployChecklist ? undefined : getStagingDeployCashData(mostRecentChecklist); + // Find PRs merged between the previous checklist's tag and the new staging tag const {mergedPRs: mergedPREntries, submoduleUpdates} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.APP_REPO); const mergedPRs = mergedPREntries.map((pr) => pr.prNumber).sort((a, b) => a - b); @@ -264,6 +274,7 @@ async function run(): Promise { core.endGroup(); console.info(`[api] Checklist PRs: ${newPRNumbers.join(', ')}`); + // Fetch Mobile-Expensify PRs (with dates for chronological grouping by submodule update) let mergedMobileExpensifyPREntries: MergedPR[] = []; try { const {mergedPRs: allMobileExpensifyPREntries} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.MOBILE_EXPENSIFY_REPO); @@ -321,6 +332,7 @@ async function run(): Promise { const {issueBody: checklistBody, issueAssignees: checklistAssignees} = await generateStagingDeployCashBodyAndAssignees(checklistParams); + // Finally, create or update the checklist const defaultPayload = { owner: CONST.GITHUB_OWNER, repo: CONST.APP_REPO, From 8b59f1eb8d8e838e2b2a39b7515fe86d711978e6 Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 21 Feb 2026 13:28:32 -0800 Subject: [PATCH 7/8] Simplify deploy blocker handling and remove unnecessary abstractions - Switch deployBlockers/resolvedDeployBlockers from URL strings to numbers in StagingDeployCashParams, generating /issues/N URLs at render time - Replace confusing two-step deploy blocker dedup with a single-pass merge - Replace isEmptyObject(find(...)) with .some() for label checks - Replace arrayDifference with Set-based filter - Inline buildNewChecklistParams at its single call site - Update stale ncc comment to explain testability rationale for fs.readFileSync Co-authored-by: Cursor --- .../javascript/isStagingDeployLocked/index.js | 49 +++--------- .github/libs/StagingDeployUtils.ts | 21 ++--- scripts/createOrUpdateStagingDeploy.ts | 77 ++++++------------- tests/unit/StagingDeployUtilsTest.ts | 17 ++-- tests/unit/createOrUpdateStagingDeployTest.ts | 28 +++---- 5 files changed, 69 insertions(+), 123 deletions(-) diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isStagingDeployLocked/index.js index 7b8a14117bf5b..02df80fcfeb34 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isStagingDeployLocked/index.js @@ -12091,10 +12091,8 @@ exports.getStagingDeployCashData = getStagingDeployCashData; exports.generateStagingDeployCashBodyAndAssignees = generateStagingDeployCashBodyAndAssignees; exports.parseChecklistSection = parseChecklistSection; const dedent_1 = __importDefault(__nccwpck_require__(6762)); -const arrayDifference_1 = __importDefault(__nccwpck_require__(7532)); const CONST_1 = __importDefault(__nccwpck_require__(9873)); const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -const isEmptyObject_1 = __nccwpck_require__(6497); /** * Generic checklist section parser. Extracts a section from the issue body, * parses checkbox items within it, and returns ChecklistItems sorted by number. @@ -12178,7 +12176,7 @@ function getStagingDeployCashData(issue) { */ async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !(0, isEmptyObject_1.isEmptyObject)(pr.labels.find((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA))) : []; + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => pr.labels.some((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA)) : []; const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); const internalQAPRMap = new Map(); for (const { number, mergerLogin } of mergerResults) { @@ -12192,9 +12190,9 @@ async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMo const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); const internalQAPRNumbers = new Set(internalQAPRMap.keys()); - const sortedPRList = [...new Set((0, arrayDifference_1.default)(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); + const sortedPRList = [...new Set(PRList.filter((n) => !internalQAPRNumbers.has(n)))].sort((a, b) => a - b); const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(a) - GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(b)); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => a - b); const sections = []; // Header let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; @@ -12234,8 +12232,13 @@ async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMo sections.push(`**Internal QA:**\n${items}\n`); } // Deploy blockers - if (deployBlockers.length > 0) { - const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); + if (sortedDeployBlockers.length > 0) { + const items = sortedDeployBlockers + .map((number) => { + const url = `${CONST_1.default.APP_REPO_URL}/issues/${number}`; + return `${resolvedDeployBlockerSet.has(number) ? '- [x] ' : '- [ ] '}${url}`; + }) + .join('\n'); sections.push(`**Deploy Blockers:**\n${items}\n`); } // Chronological section @@ -12258,38 +12261,6 @@ async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMo } -/***/ }), - -/***/ 7532: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -/** - * This function is an equivalent of _.difference, it takes two arrays and returns the difference between them. - * It returns an array of items that are in the first array but not in the second array. - */ -function arrayDifference(array1, array2) { - return [array1, array2].reduce((a, b) => a.filter((c) => !b.includes(c))); -} -exports["default"] = arrayDifference; - - -/***/ }), - -/***/ 6497: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isEmptyObject = isEmptyObject; -function isEmptyObject(obj) { - return Object.keys(obj ?? {}).length === 0; -} - - /***/ }), /***/ 6762: diff --git a/.github/libs/StagingDeployUtils.ts b/.github/libs/StagingDeployUtils.ts index 9ffee036a2678..f92a86230e95e 100644 --- a/.github/libs/StagingDeployUtils.ts +++ b/.github/libs/StagingDeployUtils.ts @@ -1,10 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type {components as OctokitComponents} from '@octokit/openapi-types/types'; import dedent from '@libs/StringUtils/dedent'; -import arrayDifference from './arrayDifference'; import CONST from './CONST'; import GithubUtils from './GithubUtils'; -import {isEmptyObject} from './isEmptyObject'; type OctokitIssueItem = OctokitComponents['schemas']['issue']; @@ -25,8 +23,8 @@ type StagingDeployCashParams = { PRListMobileExpensify?: number[]; verifiedPRList?: number[]; verifiedPRListMobileExpensify?: number[]; - deployBlockers?: string[]; - resolvedDeployBlockers?: string[]; + deployBlockers?: number[]; + resolvedDeployBlockers?: number[]; resolvedInternalQAPRs?: number[]; isSentryChecked?: boolean; isGHStatusChecked?: boolean; @@ -162,7 +160,7 @@ async function generateStagingDeployCashBodyAndAssignees({ }: StagingDeployCashParams): Promise { const data = await GithubUtils.fetchAllPullRequests(PRList); - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => !isEmptyObject(pr.labels.find((item) => item.name === CONST.LABELS.INTERNAL_QA))) : []; + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => pr.labels.some((item) => item.name === CONST.LABELS.INTERNAL_QA)) : []; const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({number: pr.number, mergerLogin})))); const internalQAPRMap = new Map(); @@ -180,9 +178,9 @@ async function generateStagingDeployCashBodyAndAssignees({ const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); const internalQAPRNumbers = new Set(internalQAPRMap.keys()); - const sortedPRList = [...new Set(arrayDifference(PRList, [...internalQAPRNumbers]))].sort((a, b) => a - b); + const sortedPRList = [...new Set(PRList.filter((n) => !internalQAPRNumbers.has(n)))].sort((a, b) => a - b); const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => GithubUtils.getIssueOrPullRequestNumberFromURL(a) - GithubUtils.getIssueOrPullRequestNumberFromURL(b)); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => a - b); const sections: string[] = []; @@ -228,8 +226,13 @@ async function generateStagingDeployCashBodyAndAssignees({ } // Deploy blockers - if (deployBlockers.length > 0) { - const items = sortedDeployBlockers.map((url) => `${resolvedDeployBlockerSet.has(url) ? '- [x] ' : '- [ ] '}${url}`).join('\n'); + if (sortedDeployBlockers.length > 0) { + const items = sortedDeployBlockers + .map((number) => { + const url = `${CONST.APP_REPO_URL}/issues/${number}`; + return `${resolvedDeployBlockerSet.has(number) ? '- [x] ' : '- [ ] '}${url}`; + }) + .join('\n'); sections.push(`**Deploy Blockers:**\n${items}\n`); } diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateStagingDeploy.ts index 87c621d99b577..df4d7c20024a9 100644 --- a/scripts/createOrUpdateStagingDeploy.ts +++ b/scripts/createOrUpdateStagingDeploy.ts @@ -12,10 +12,6 @@ import type {ChecklistItem, StagingDeployCashData} from '@github/libs/StagingDep type IssuesCreateResponse = Awaited>['data']; -type PackageJson = { - version: string; -}; - type TimelineEntry = {type: 'pr'; prNumber: number; date: string} | {type: 'submodule'; version: string; date: string; commit: string}; /** @@ -112,31 +108,6 @@ async function buildChronologicalSection({ return section; } -/** - * Build params for creating a brand-new deploy checklist. - */ -function buildNewChecklistParams({ - newVersion, - newPRNumbers, - mergedMobileExpensifyPREntries, - previousChecklistData, - chronologicalSection, -}: { - newVersion: string; - newPRNumbers: number[]; - mergedMobileExpensifyPREntries: MergedPR[]; - previousChecklistData: StagingDeployCashData; - chronologicalSection: string; -}) { - return { - tag: newVersion, - PRList: newPRNumbers, - PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => pr.prNumber), - previousTag: previousChecklistData.version, - chronologicalSection, - }; -} - /** * Build params for updating an existing open deploy checklist, preserving checked state. */ @@ -185,16 +156,18 @@ async function buildUpdateChecklistParams({ labels: CONST.LABELS.DEPLOY_BLOCKER, }); - const deployBlockers = preserveCheckboxState( - openDeployBlockers.map((db) => ({number: db.number, url: db.html_url})), - currentChecklistData.deployBlockers, - ); - - // Add demoted/closed blockers from current checklist and auto-check them - for (const deployBlocker of currentChecklistData.deployBlockers) { - const isChecked = !deployBlockers.some((openBlocker) => openBlocker.number === deployBlocker.number); - deployBlockers.push({...deployBlocker, isChecked}); - } + // Merge open blockers (from the API) with previously-tracked blockers (from the current checklist). + // Open blockers preserve their checked state; closed/demoted blockers are auto-checked. + const openBlockerNumbers = new Set(openDeployBlockers.map((db) => db.number)); + const allBlockerNumbers = new Set([...openBlockerNumbers, ...currentChecklistData.deployBlockers.map((db) => db.number)]); + const deployBlockers: ChecklistItem[] = [...allBlockerNumbers].map((number) => { + const existing = currentChecklistData.deployBlockers.find((db) => db.number === number); + if (!openBlockerNumbers.has(number)) { + return {number, url: existing?.url ?? '', isChecked: true}; + } + const url = existing?.url ?? openDeployBlockers.find((db) => db.number === number)?.html_url ?? ''; + return {number, url, isChecked: existing?.isChecked ?? false}; + }); const didVersionChange = newVersion !== currentChecklistData.version; @@ -204,8 +177,8 @@ async function buildUpdateChecklistParams({ PRListMobileExpensify: PRListMobileExpensify.map((pr) => pr.number), verifiedPRList: PRList.filter((pr) => pr.isChecked).map((pr) => pr.number), verifiedPRListMobileExpensify: PRListMobileExpensify.filter((pr) => pr.isChecked).map((pr) => pr.number), - deployBlockers: deployBlockers.map((blocker) => blocker.url), - resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isChecked).map((blocker) => blocker.url), + deployBlockers: deployBlockers.map((blocker) => blocker.number), + resolvedDeployBlockers: deployBlockers.filter((blocker) => blocker.isChecked).map((blocker) => blocker.number), resolvedInternalQAPRs: currentChecklistData.internalQAPRList.filter((pr) => pr.isChecked).map((pr) => pr.number), isSentryChecked: didVersionChange ? false : currentChecklistData.isSentryChecked, isGHStatusChecked: didVersionChange ? false : currentChecklistData.isGHStatusChecked, @@ -215,12 +188,10 @@ async function buildUpdateChecklistParams({ } async function run(): Promise { - // Note: require('package.json').version does not work because ncc will resolve that to a plain string at compile time - const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')) as PackageJson; - // e.g. '1.2.3-4' - const newVersion = packageJson.version; + // Read version at runtime (not via static import) so tests can mock different versions per test case + const {version: newVersion} = JSON.parse(fs.readFileSync('package.json', 'utf8')) as {version: string}; // e.g. '1.2.3-4-staging' - const newStagingTag = `${packageJson.version}-staging`; + const newStagingTag = `${newVersion}-staging`; try { const {data: recentDeployChecklists} = await GithubUtils.octokit.issues.listForRepo({ @@ -310,13 +281,13 @@ async function run(): Promise { let checklistParams; if (shouldCreateNewDeployChecklist) { - checklistParams = buildNewChecklistParams({ - newVersion, - newPRNumbers, - mergedMobileExpensifyPREntries, - previousChecklistData, + checklistParams = { + tag: newVersion, + PRList: newPRNumbers, + PRListMobileExpensify: mergedMobileExpensifyPREntries.map((pr) => pr.prNumber), + previousTag: previousChecklistData.version, chronologicalSection, - }); + }; } else if (currentChecklistData) { checklistParams = await buildUpdateChecklistParams({ newVersion, @@ -367,4 +338,4 @@ if (require.main === module) { } export default run; -export {buildChronologicalSection, buildNewChecklistParams, buildUpdateChecklistParams, preserveCheckboxState}; +export {buildChronologicalSection, buildUpdateChecklistParams, preserveCheckboxState}; diff --git a/tests/unit/StagingDeployUtilsTest.ts b/tests/unit/StagingDeployUtilsTest.ts index 44e0686d7da0a..49a7ddcf02eb6 100644 --- a/tests/unit/StagingDeployUtilsTest.ts +++ b/tests/unit/StagingDeployUtilsTest.ts @@ -307,7 +307,7 @@ describe('StagingDeployUtils', () => { const PRListMobileExpensify = [1, 2, 3]; const internalQAPRList = [6, 7]; - const baseDeployBlockerList = [`https://github.com/${process.env.GITHUB_REPOSITORY}/pull/3`, `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/4`]; + const baseDeployBlockerList = [3, 4]; const baseExpectedOutput = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; const openCheckbox = '- [ ] '; @@ -324,6 +324,7 @@ describe('StagingDeployUtils', () => { const ghVerification = 'I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions.'; const prURL = (n: number) => `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/${n}`; + const issueURL = (n: number) => `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${n}`; const mobileURL = (n: number) => `https://github.com/Expensify/Mobile-Expensify/pull/${n}`; const allVerifiedExpectedOutput = @@ -406,8 +407,8 @@ describe('StagingDeployUtils', () => { expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${openCheckbox}${issueURL(3)}` + + `${lineBreak}${openCheckbox}${issueURL(4)}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + @@ -423,13 +424,13 @@ describe('StagingDeployUtils', () => { PRList: basePRList, verifiedPRList: basePRList, deployBlockers: baseDeployBlockerList, - resolvedDeployBlockers: [baseDeployBlockerList.at(0) ?? ''], + resolvedDeployBlockers: [baseDeployBlockerList.at(0) ?? 0], }); expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${openCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${closedCheckbox}${issueURL(3)}` + + `${lineBreak}${openCheckbox}${issueURL(4)}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + @@ -455,8 +456,8 @@ describe('StagingDeployUtils', () => { `${lineBreak}${closedCheckbox}${prURL(4)}` + `${lineBreak}${closedCheckbox}${prURL(5)}` + `${lineBreak}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(0)}` + - `${lineBreak}${closedCheckbox}${baseDeployBlockerList.at(1)}` + + `${lineBreak}${closedCheckbox}${issueURL(3)}` + + `${lineBreak}${closedCheckbox}${issueURL(4)}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease}` + `${lineBreak}${openCheckbox}${sentryVerificationPreviousRelease}` + diff --git a/tests/unit/createOrUpdateStagingDeployTest.ts b/tests/unit/createOrUpdateStagingDeployTest.ts index 9d63caba9f5e4..0b9ca037c7881 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.ts +++ b/tests/unit/createOrUpdateStagingDeployTest.ts @@ -139,7 +139,7 @@ const baseMobileExpensifyPRList = [ `https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/22`, ]; -const baseIssueList = [`https://github.com/${process.env.GITHUB_REPOSITORY}/issues/11`, `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/12`]; +const issueURL = (n: number) => `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${n}`; // eslint-disable-next-line max-len const baseExpectedOutput = (version = '1.0.2-1', includeMobileExpensifyCompare = true) => // cspell:disable @@ -448,11 +448,11 @@ describe('createOrUpdateStagingDeployCash', () => { `${lineBreak}${openCheckbox}https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/23` + `${lineBreak}${openCheckbox}https://github.com/${CONST.GITHUB_OWNER}/${CONST.MOBILE_EXPENSIFY_REPO}/pull/24` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${basePRList.at(5)}` + - `${lineBreak}${openCheckbox}${basePRList.at(8)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(1)}` + + `${lineBreak}${openCheckbox}${issueURL(6)}` + + `${lineBreak}${openCheckbox}${issueURL(9)}` + + `${lineBreak}${closedCheckbox}${issueURL(10)}` + + `${lineBreak}${openCheckbox}${issueURL(11)}` + + `${lineBreak}${openCheckbox}${issueURL(12)}` + `${lineBreakDouble}${buildChronologicalSection([...baseNewPullRequests, ...newPullRequests], [20, 21, 22, 23, 24])}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${openCheckbox}${sentryVerificationCurrentRelease('1.0.2-2')}` + @@ -524,11 +524,11 @@ describe('createOrUpdateStagingDeployCash', () => { `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(1)}` + `${lineBreak}${openCheckbox}${baseMobileExpensifyPRList.at(2)}` + `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${closedCheckbox}${basePRList.at(5)}` + - `${lineBreak}${openCheckbox}${basePRList.at(8)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(0)}` + - `${lineBreak}${openCheckbox}${baseIssueList.at(1)}` + + `${lineBreak}${closedCheckbox}${issueURL(6)}` + + `${lineBreak}${openCheckbox}${issueURL(9)}` + + `${lineBreak}${closedCheckbox}${issueURL(10)}` + + `${lineBreak}${openCheckbox}${issueURL(11)}` + + `${lineBreak}${openCheckbox}${issueURL(12)}` + `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests, [20, 21, 22])}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + @@ -581,9 +581,9 @@ describe('createOrUpdateStagingDeployCash', () => { `${lineBreak}${openCheckbox}${basePRList.at(7)}` + // Note: No Mobile-Expensify PRs section since there are none `${lineBreakDouble}${deployBlockerHeader}` + - `${lineBreak}${openCheckbox}${basePRList.at(5)}` + - `${lineBreak}${openCheckbox}${basePRList.at(8)}` + - `${lineBreak}${closedCheckbox}${basePRList.at(9)}` + + `${lineBreak}${openCheckbox}${issueURL(6)}` + + `${lineBreak}${openCheckbox}${issueURL(9)}` + + `${lineBreak}${closedCheckbox}${issueURL(10)}` + `${lineBreakDouble}${buildChronologicalSection(baseNewPullRequests)}` + `${lineBreak}${deployerVerificationsHeader}` + `${lineBreak}${closedCheckbox}${sentryVerificationCurrentRelease('1.0.2-1')}` + From 85db0e3d674f0321044b464434d9c3fd5dddc7c0 Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 21 Feb 2026 13:49:09 -0800 Subject: [PATCH 8/8] Rename StagingDeployCash to DeployChecklist throughout codebase Rename all internal code references from "StagingDeployCash" / "stagingDeploy" to "deployChecklist" / "DeployChecklist" to improve readability. The actual GitHub label string 'StagingDeployCash' remains unchanged. Renames files, types, functions, variables, log messages, imports, workflow steps, and action directories. The awaitStagingDeploys action is unchanged (separate concept from the checklist). Co-authored-by: Cursor --- .../isDeployChecklistLocked/action.yml | 14 + .../index.js | 384 +++++++++--------- .../isDeployChecklistLocked.ts} | 8 +- .../isStagingDeployLocked/action.yml | 14 - .../markPullRequestsAsDeployed/index.js | 8 +- .../markPullRequestsAsDeployed.ts | 8 +- ...DeployUtils.ts => DeployChecklistUtils.ts} | 40 +- .github/scripts/buildActions.sh | 2 +- .github/workflows/createDeployChecklist.yml | 2 +- .github/workflows/finishReleaseCycle.yml | 2 +- .github/workflows/preDeploy.yml | 8 +- ...oy.ts => createOrUpdateDeployChecklist.ts} | 22 +- ...ilsTest.ts => DeployChecklistUtilsTest.ts} | 46 +-- ...s => createOrUpdateDeployChecklistTest.ts} | 66 +-- ...Test.ts => isDeployChecklistLockedTest.ts} | 20 +- 15 files changed, 322 insertions(+), 322 deletions(-) create mode 100644 .github/actions/javascript/isDeployChecklistLocked/action.yml rename .github/actions/javascript/{isStagingDeployLocked => isDeployChecklistLocked}/index.js (99%) rename .github/actions/javascript/{isStagingDeployLocked/isStagingDeployLocked.ts => isDeployChecklistLocked/isDeployChecklistLocked.ts} (71%) delete mode 100644 .github/actions/javascript/isStagingDeployLocked/action.yml rename .github/libs/{StagingDeployUtils.ts => DeployChecklistUtils.ts} (87%) rename scripts/{createOrUpdateStagingDeploy.ts => createOrUpdateDeployChecklist.ts} (93%) rename tests/unit/{StagingDeployUtilsTest.ts => DeployChecklistUtilsTest.ts} (90%) rename tests/unit/{createOrUpdateStagingDeployTest.ts => createOrUpdateDeployChecklistTest.ts} (95%) rename tests/unit/{isStagingDeployLockedTest.ts => isDeployChecklistLockedTest.ts} (60%) diff --git a/.github/actions/javascript/isDeployChecklistLocked/action.yml b/.github/actions/javascript/isDeployChecklistLocked/action.yml new file mode 100644 index 0000000000000..f79f208d0e337 --- /dev/null +++ b/.github/actions/javascript/isDeployChecklistLocked/action.yml @@ -0,0 +1,14 @@ +name: 'Check if the deploy checklist is locked' +description: 'Checks if the open deploy checklist issue has the LockCashDeploys label' +inputs: + GITHUB_TOKEN: + description: Auth token for New Expensify Github + required: true +outputs: + IS_LOCKED: + description: Whether or not the open deploy checklist issue is locked. + NUMBER: + description: Deploy checklist issue number +runs: + using: 'node20' + main: 'index.js' diff --git a/.github/actions/javascript/isStagingDeployLocked/index.js b/.github/actions/javascript/isDeployChecklistLocked/index.js similarity index 99% rename from .github/actions/javascript/isStagingDeployLocked/index.js rename to .github/actions/javascript/isDeployChecklistLocked/index.js index 02df80fcfeb34..f4be6a8de03bd 100644 --- a/.github/actions/javascript/isStagingDeployLocked/index.js +++ b/.github/actions/javascript/isDeployChecklistLocked/index.js @@ -11494,7 +11494,7 @@ function wrappy (fn, cb) { /***/ }), -/***/ 2137: +/***/ 6971: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -11534,9 +11534,9 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", ({ value: true })); const core = __importStar(__nccwpck_require__(2186)); -const StagingDeployUtils_1 = __nccwpck_require__(3318); +const DeployChecklistUtils_1 = __nccwpck_require__(2141); const run = function () { - return (0, StagingDeployUtils_1.getStagingDeployCash)() + return (0, DeployChecklistUtils_1.getDeployChecklist)() .then(({ labels, number }) => { const labelsNames = labels.map((label) => { if (typeof label === 'string') { @@ -11544,12 +11544,12 @@ const run = function () { } return label.name; }); - console.log(`Found StagingDeployCash with labels: ${JSON.stringify(labelsNames)}`); + console.log(`Found deploy checklist with labels: ${JSON.stringify(labelsNames)}`); core.setOutput('IS_LOCKED', labelsNames.includes('🔐 LockCashDeploys 🔐')); core.setOutput('NUMBER', number); }) .catch((err) => { - console.warn('No open StagingDeployCash found, continuing...', err); + console.warn('No open deploy checklist found, continuing...', err); core.setOutput('IS_LOCKED', false); core.setOutput('NUMBER', 0); }); @@ -11638,6 +11638,192 @@ const CONST = { exports["default"] = CONST; +/***/ }), + +/***/ 2141: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getDeployChecklist = getDeployChecklist; +exports.getDeployChecklistData = getDeployChecklistData; +exports.generateDeployChecklistBodyAndAssignees = generateDeployChecklistBodyAndAssignees; +exports.parseChecklistSection = parseChecklistSection; +const dedent_1 = __importDefault(__nccwpck_require__(6762)); +const CONST_1 = __importDefault(__nccwpck_require__(9873)); +const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); +/** + * Generic checklist section parser. Extracts a section from the issue body, + * parses checkbox items within it, and returns ChecklistItems sorted by number. + */ +function parseChecklistSection(issueBody, sectionRegex, itemRegex, urlTransform) { + const sectionMatch = issueBody?.match(sectionRegex) ?? null; + if (sectionMatch?.length !== 2) { + return []; + } + const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { + const rawUrl = match[2]; + return { + url: urlTransform ? urlTransform(rawUrl) : rawUrl, + number: Number.parseInt(match[3], 10), + isChecked: match[1] === 'x', + }; + }); + return items.sort((a, b) => a.number - b.number); +} +function getDeployChecklistPRList(issue) { + const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g')); + if (result.length === 0) { + console.log('Hmmm...The open deploy checklist does not list any pull requests, continuing...'); + } + return result; +} +function getDeployChecklistPRListMobileExpensify(issue) { + return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); +} +function getDeployChecklistDeployBlockers(issue) { + return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); +} +function getDeployChecklistInternalQA(issue) { + return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), (url) => url.split('-').at(0)?.trim() ?? ''); +} +async function getDeployChecklist() { + const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ + owner: CONST_1.default.GITHUB_OWNER, + repo: CONST_1.default.APP_REPO, + labels: CONST_1.default.LABELS.STAGING_DEPLOY, + state: 'open', + }); + if (!data.length) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + if (data.length > 1) { + throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + const issue = data.at(0); + if (!issue) { + throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); + } + return getDeployChecklistData(issue); +} +function getDeployChecklistData(issue) { + try { + const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); + const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); + return { + title: issue.title, + url: issue.url, + number: GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(issue.url), + labels: issue.labels, + PRList: getDeployChecklistPRList(issue), + PRListMobileExpensify: getDeployChecklistPRListMobileExpensify(issue), + deployBlockers: getDeployChecklistDeployBlockers(issue), + internalQAPRList: getDeployChecklistInternalQA(issue), + isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, + isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, + version, + tag: `${version}-staging`, + }; + } + catch (exception) { + throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); + } +} +/** + * Generate the issue body and assignees for a deploy checklist. + * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. + */ +async function generateDeployChecklistBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { + const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); + const internalQAPRs = Array.isArray(data) ? data.filter((pr) => pr.labels.some((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA)) : []; + const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); + const internalQAPRMap = new Map(); + for (const { number, mergerLogin } of mergerResults) { + internalQAPRMap.set(number, mergerLogin); + } + console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); + const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; + console.log('Found the following NO QA PRs:', noQAPRNumbers); + const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); + const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); + const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); + const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); + const internalQAPRNumbers = new Set(internalQAPRMap.keys()); + const sortedPRList = [...new Set(PRList.filter((n) => !internalQAPRNumbers.has(n)))].sort((a, b) => a - b); + const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); + const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => a - b); + const sections = []; + // Header + let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; + if (sortedPRListMobileExpensify.length > 0) { + header += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; + } + sections.push(header); + // PR list + if (sortedPRList.length > 0) { + const items = sortedPRList + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); + } + // Mobile-Expensify PR list + if (sortedPRListMobileExpensify.length > 0) { + const items = sortedPRListMobileExpensify + .map((prNumber) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); + return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; + }) + .join('\n'); + sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); + } + // Internal QA PR list + if (internalQAPRMap.size > 0) { + console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); + const items = [...internalQAPRMap.entries()] + .map(([prNumber, merger]) => { + const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); + return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; + }) + .join('\n'); + sections.push(`**Internal QA:**\n${items}\n`); + } + // Deploy blockers + if (sortedDeployBlockers.length > 0) { + const items = sortedDeployBlockers + .map((number) => { + const url = `${CONST_1.default.APP_REPO_URL}/issues/${number}`; + return `${resolvedDeployBlockerSet.has(number) ? '- [x] ' : '- [ ] '}${url}`; + }) + .join('\n'); + sections.push(`**Deploy Blockers:**\n${items}\n`); + } + // Chronological section + if (chronologicalSection) { + sections.push(chronologicalSection); + } + // Deployer verifications + const check = (checked) => (checked ? 'x' : ' '); + sections.push((0, dedent_1.default)(` + **Deployer verifications:** + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). + - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. + `).trimEnd()); + // Footer + sections.push('cc @Expensify/applauseleads\n'); + const issueBody = sections.join('\n'); + const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login) => login !== undefined); + return { issueBody, issueAssignees }; +} + + /***/ }), /***/ 9296: @@ -12075,192 +12261,6 @@ class GithubUtils { exports["default"] = GithubUtils; -/***/ }), - -/***/ 3318: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getStagingDeployCash = getStagingDeployCash; -exports.getStagingDeployCashData = getStagingDeployCashData; -exports.generateStagingDeployCashBodyAndAssignees = generateStagingDeployCashBodyAndAssignees; -exports.parseChecklistSection = parseChecklistSection; -const dedent_1 = __importDefault(__nccwpck_require__(6762)); -const CONST_1 = __importDefault(__nccwpck_require__(9873)); -const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); -/** - * Generic checklist section parser. Extracts a section from the issue body, - * parses checkbox items within it, and returns ChecklistItems sorted by number. - */ -function parseChecklistSection(issueBody, sectionRegex, itemRegex, urlTransform) { - const sectionMatch = issueBody?.match(sectionRegex) ?? null; - if (sectionMatch?.length !== 2) { - return []; - } - const items = [...sectionMatch[1].matchAll(itemRegex)].map((match) => { - const rawUrl = match[2]; - return { - url: urlTransform ? urlTransform(rawUrl) : rawUrl, - number: Number.parseInt(match[3], 10), - isChecked: match[1] === 'x', - }; - }); - return items.sort((a, b) => a.number - b.number); -} -function getStagingDeployCashPRList(issue) { - const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g')); - if (result.length === 0) { - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); - } - return result; -} -function getStagingDeployCashPRListMobileExpensify(issue) { - return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); -} -function getStagingDeployCashDeployBlockers(issue) { - return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); -} -function getStagingDeployCashInternalQA(issue) { - return parseChecklistSection(issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST_1.default.PULL_REQUEST_REGEX.source})`, 'g'), (url) => url.split('-').at(0)?.trim() ?? ''); -} -async function getStagingDeployCash() { - const { data } = await GithubUtils_1.default.octokit.issues.listForRepo({ - owner: CONST_1.default.GITHUB_OWNER, - repo: CONST_1.default.APP_REPO, - labels: CONST_1.default.LABELS.STAGING_DEPLOY, - state: 'open', - }); - if (!data.length) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - if (data.length > 1) { - throw new Error(`Found more than one ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - const issue = data.at(0); - if (!issue) { - throw new Error(`Found an undefined ${CONST_1.default.LABELS.STAGING_DEPLOY} issue.`); - } - return getStagingDeployCashData(issue); -} -function getStagingDeployCashData(issue) { - try { - const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); - const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); - return { - title: issue.title, - url: issue.url, - number: GithubUtils_1.default.getIssueOrPullRequestNumberFromURL(issue.url), - labels: issue.labels, - PRList: getStagingDeployCashPRList(issue), - PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: getStagingDeployCashDeployBlockers(issue), - internalQAPRList: getStagingDeployCashInternalQA(issue), - isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, - isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, - version, - tag: `${version}-staging`, - }; - } - catch (exception) { - throw new Error(`Unable to find ${CONST_1.default.LABELS.STAGING_DEPLOY} issue with correct data.`); - } -} -/** - * Generate the issue body and assignees for a StagingDeployCash checklist. - * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. - */ -async function generateStagingDeployCashBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], verifiedPRList = [], verifiedPRListMobileExpensify = [], deployBlockers = [], resolvedDeployBlockers = [], resolvedInternalQAPRs = [], isSentryChecked = false, isGHStatusChecked = false, previousTag = '', chronologicalSection = '', }) { - const data = await GithubUtils_1.default.fetchAllPullRequests(PRList); - const internalQAPRs = Array.isArray(data) ? data.filter((pr) => pr.labels.some((item) => item.name === CONST_1.default.LABELS.INTERNAL_QA)) : []; - const mergerResults = await Promise.all(internalQAPRs.map((pr) => GithubUtils_1.default.getPullRequestMergerLogin(pr.number).then((mergerLogin) => ({ number: pr.number, mergerLogin })))); - const internalQAPRMap = new Map(); - for (const { number, mergerLogin } of mergerResults) { - internalQAPRMap.set(number, mergerLogin); - } - console.log('Found the following Internal QA PRs:', Object.fromEntries(internalQAPRMap)); - const noQAPRNumbers = Array.isArray(data) ? data.filter((PR) => /\[No\s?QA]/i.test(PR.title)).map((item) => item.number) : []; - console.log('Found the following NO QA PRs:', noQAPRNumbers); - const verifiedAppPRs = new Set([...verifiedPRList, ...noQAPRNumbers]); - const verifiedMobileExpensifyPRs = new Set(verifiedPRListMobileExpensify); - const resolvedInternalQAPRSet = new Set(resolvedInternalQAPRs); - const resolvedDeployBlockerSet = new Set(resolvedDeployBlockers); - const internalQAPRNumbers = new Set(internalQAPRMap.keys()); - const sortedPRList = [...new Set(PRList.filter((n) => !internalQAPRNumbers.has(n)))].sort((a, b) => a - b); - const sortedPRListMobileExpensify = [...new Set(PRListMobileExpensify)].sort((a, b) => a - b); - const sortedDeployBlockers = [...new Set(deployBlockers)].sort((a, b) => a - b); - const sections = []; - // Header - let header = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n`; - if (sortedPRListMobileExpensify.length > 0) { - header += `**Mobile-Expensify Changes:** https://github.com/${CONST_1.default.GITHUB_OWNER}/${CONST_1.default.MOBILE_EXPENSIFY_REPO}/compare/production...staging\n`; - } - sections.push(header); - // PR list - if (sortedPRList.length > 0) { - const items = sortedPRList - .map((prNumber) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); - return `${verifiedAppPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; - }) - .join('\n'); - sections.push(`**This release contains changes from the following pull requests:**\n${items}\n`); - } - // Mobile-Expensify PR list - if (sortedPRListMobileExpensify.length > 0) { - const items = sortedPRListMobileExpensify - .map((prNumber) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.MOBILE_EXPENSIFY_URL); - return `${verifiedMobileExpensifyPRs.has(prNumber) ? '- [x]' : '- [ ]'} ${url}`; - }) - .join('\n'); - sections.push(`**Mobile-Expensify PRs:**\n${items}\n`); - } - // Internal QA PR list - if (internalQAPRMap.size > 0) { - console.log('Found the following verified Internal QA PRs:', resolvedInternalQAPRs); - const items = [...internalQAPRMap.entries()] - .map(([prNumber, merger]) => { - const url = GithubUtils_1.default.getPullRequestURLFromNumber(prNumber, CONST_1.default.APP_REPO_URL); - return `${resolvedInternalQAPRSet.has(prNumber) ? '- [x]' : '- [ ]'} ${url} - @${merger}`; - }) - .join('\n'); - sections.push(`**Internal QA:**\n${items}\n`); - } - // Deploy blockers - if (sortedDeployBlockers.length > 0) { - const items = sortedDeployBlockers - .map((number) => { - const url = `${CONST_1.default.APP_REPO_URL}/issues/${number}`; - return `${resolvedDeployBlockerSet.has(number) ? '- [x] ' : '- [ ] '}${url}`; - }) - .join('\n'); - sections.push(`**Deploy Blockers:**\n${items}\n`); - } - // Chronological section - if (chronologicalSection) { - sections.push(chronologicalSection); - } - // Deployer verifications - const check = (checked) => (checked ? 'x' : ' '); - sections.push((0, dedent_1.default)(` - **Deployer verifications:** - - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${tag}/?project=app&environment=staging) for **this release version** and verified that this release does not introduce any new crashes. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). - - [${check(isSentryChecked)}] I checked [Sentry](https://expensify.sentry.io/releases/new.expensify%40${previousTag}/?project=app&environment=production) for **the previous release version** and verified that the release did not introduce any new crashes. Because mobile deploys use a phased rollout, completing this checklist will deploy the previous release version to 100% of users. More detailed instructions on this verification can be found [here](https://stackoverflowteams.com/c/expensify/questions/15095/15096). - - [${check(isGHStatusChecked)}] I checked [GitHub Status](https://www.githubstatus.com/) and verified there is no reported incident with Actions. - `).trimEnd()); - // Footer - sections.push('cc @Expensify/applauseleads\n'); - const issueBody = sections.join('\n'); - const issueAssignees = [...new Set(internalQAPRMap.values())].filter((login) => login !== undefined); - return { issueBody, issueAssignees }; -} - - /***/ }), /***/ 6762: @@ -12506,7 +12506,7 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(2137); +/******/ var __webpack_exports__ = __nccwpck_require__(6971); /******/ module.exports = __webpack_exports__; /******/ /******/ })() diff --git a/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts b/.github/actions/javascript/isDeployChecklistLocked/isDeployChecklistLocked.ts similarity index 71% rename from .github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts rename to .github/actions/javascript/isDeployChecklistLocked/isDeployChecklistLocked.ts index d8cdfa2675db0..0a94482aa0437 100644 --- a/.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked.ts +++ b/.github/actions/javascript/isDeployChecklistLocked/isDeployChecklistLocked.ts @@ -1,8 +1,8 @@ import * as core from '@actions/core'; -import {getStagingDeployCash} from '@github/libs/StagingDeployUtils'; +import {getDeployChecklist} from '@github/libs/DeployChecklistUtils'; const run = function (): Promise { - return getStagingDeployCash() + return getDeployChecklist() .then(({labels, number}) => { const labelsNames = labels.map((label) => { if (typeof label === 'string') { @@ -10,12 +10,12 @@ const run = function (): Promise { } return label.name; }); - console.log(`Found StagingDeployCash with labels: ${JSON.stringify(labelsNames)}`); + console.log(`Found deploy checklist with labels: ${JSON.stringify(labelsNames)}`); core.setOutput('IS_LOCKED', labelsNames.includes('🔐 LockCashDeploys 🔐')); core.setOutput('NUMBER', number); }) .catch((err) => { - console.warn('No open StagingDeployCash found, continuing...', err); + console.warn('No open deploy checklist found, continuing...', err); core.setOutput('IS_LOCKED', false); core.setOutput('NUMBER', 0); }); diff --git a/.github/actions/javascript/isStagingDeployLocked/action.yml b/.github/actions/javascript/isStagingDeployLocked/action.yml deleted file mode 100644 index 395a081a76200..0000000000000 --- a/.github/actions/javascript/isStagingDeployLocked/action.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 'Check if the StagingDeployCash is locked' -description: 'Checks if the open StagingDeployCash issue has the LockCashDeploys label' -inputs: - GITHUB_TOKEN: - description: Auth token for New Expensify Github - required: true -outputs: - IS_LOCKED: - description: Whether or not the open StagingDeployCash issue is locked. - NUMBER: - description: StagingDeployCash issue number -runs: - using: 'node20' - main: 'index.js' diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index ef6af6b9be185..29baef2a7fa88 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -12786,9 +12786,9 @@ async function commentPR(PR, message, repo = github_1.context.repo.repo) { const workflowURL = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; const getCommit = (0, memoize_1.default)(GithubUtils_1.default.octokit.git.getCommit); /** - * Process staging deploy comments for a list of PRs + * Process deploy checklist comments for a list of PRs */ -async function commentStagingDeployPRs(prList, repoName, recentTags, getDeployMessage) { +async function commentDeployChecklistPRs(prList, repoName, recentTags, getDeployMessage) { for (const prNumber of prList) { try { const { data: pr } = await GithubUtils_1.default.octokit.pulls.get({ @@ -12914,10 +12914,10 @@ async function run() { } } // Comment on the PRs - await commentStagingDeployPRs(prList, CONST_1.default.APP_REPO, appRecentTags, getDeployMessage); + await commentDeployChecklistPRs(prList, CONST_1.default.APP_REPO, appRecentTags, getDeployMessage); console.log(`✅ Added staging deploy comment ${prList.length} App PRs`); if (mobileExpensifyPRList.length > 0) { - await commentStagingDeployPRs(mobileExpensifyPRList, CONST_1.default.MOBILE_EXPENSIFY_REPO, mobileExpensifyRecentTags, getDeployMessage); + await commentDeployChecklistPRs(mobileExpensifyPRList, CONST_1.default.MOBILE_EXPENSIFY_REPO, mobileExpensifyRecentTags, getDeployMessage); console.log(`✅ Completed staging deploy comment on ${mobileExpensifyPRList.length} Mobile-Expensify PRs`); } } diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index bf7ce3c35f444..641f3e54faf13 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -43,9 +43,9 @@ const workflowURL = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOS const getCommit = memoize(GithubUtils.octokit.git.getCommit); /** - * Process staging deploy comments for a list of PRs + * Process deploy checklist comments for a list of PRs */ -async function commentStagingDeployPRs( +async function commentDeployChecklistPRs( prList: number[], repoName: string, recentTags: Awaited>['data'], @@ -186,11 +186,11 @@ async function run() { } // Comment on the PRs - await commentStagingDeployPRs(prList, CONST.APP_REPO, appRecentTags, getDeployMessage); + await commentDeployChecklistPRs(prList, CONST.APP_REPO, appRecentTags, getDeployMessage); console.log(`✅ Added staging deploy comment ${prList.length} App PRs`); if (mobileExpensifyPRList.length > 0) { - await commentStagingDeployPRs(mobileExpensifyPRList, CONST.MOBILE_EXPENSIFY_REPO, mobileExpensifyRecentTags, getDeployMessage); + await commentDeployChecklistPRs(mobileExpensifyPRList, CONST.MOBILE_EXPENSIFY_REPO, mobileExpensifyRecentTags, getDeployMessage); console.log(`✅ Completed staging deploy comment on ${mobileExpensifyPRList.length} Mobile-Expensify PRs`); } } diff --git a/.github/libs/StagingDeployUtils.ts b/.github/libs/DeployChecklistUtils.ts similarity index 87% rename from .github/libs/StagingDeployUtils.ts rename to .github/libs/DeployChecklistUtils.ts index f92a86230e95e..86c7353e9cea8 100644 --- a/.github/libs/StagingDeployUtils.ts +++ b/.github/libs/DeployChecklistUtils.ts @@ -12,12 +12,12 @@ type ChecklistItem = { isChecked: boolean; }; -type StagingDeployCashBody = { +type DeployChecklistBody = { issueBody: string; issueAssignees: string[]; }; -type StagingDeployCashParams = { +type DeployChecklistParams = { tag: string; PRList: number[]; PRListMobileExpensify?: number[]; @@ -32,7 +32,7 @@ type StagingDeployCashParams = { chronologicalSection?: string; }; -type StagingDeployCashData = { +type DeployChecklistData = { title: string; url: string; number: number; @@ -67,23 +67,23 @@ function parseChecklistSection(issueBody: string | null | undefined, sectionRege return items.sort((a, b) => a.number - b.number); } -function getStagingDeployCashPRList(issue: OctokitIssueItem): ChecklistItem[] { +function getDeployChecklistPRList(issue: OctokitIssueItem): ChecklistItem[] { const result = parseChecklistSection(issue.body, /pull requests:\*\*\r?\n((?:-.*\r?\n)+)\r?\n\r?\n?/, new RegExp(`- \\[([ x])] (${CONST.PULL_REQUEST_REGEX.source})`, 'g')); if (result.length === 0) { - console.log('Hmmm...The open StagingDeployCash does not list any pull requests, continuing...'); + console.log('Hmmm...The open deploy checklist does not list any pull requests, continuing...'); } return result; } -function getStagingDeployCashPRListMobileExpensify(issue: OctokitIssueItem): ChecklistItem[] { +function getDeployChecklistPRListMobileExpensify(issue: OctokitIssueItem): ChecklistItem[] { return parseChecklistSection(issue.body, /Mobile-Expensify PRs:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } -function getStagingDeployCashDeployBlockers(issue: OctokitIssueItem): ChecklistItem[] { +function getDeployChecklistDeployBlockers(issue: OctokitIssueItem): ChecklistItem[] { return parseChecklistSection(issue.body, /Deploy Blockers:\*\*\r?\n((?:-.*\r?\n)+)/, new RegExp(`- \\[([ x])]\\s(${CONST.ISSUE_OR_PULL_REQUEST_REGEX.source})`, 'g')); } -function getStagingDeployCashInternalQA(issue: OctokitIssueItem): ChecklistItem[] { +function getDeployChecklistInternalQA(issue: OctokitIssueItem): ChecklistItem[] { return parseChecklistSection( issue.body, /Internal QA:\*\*\r?\n((?:- \[[ x]].*\r?\n)+)/, @@ -92,7 +92,7 @@ function getStagingDeployCashInternalQA(issue: OctokitIssueItem): ChecklistItem[ ); } -async function getStagingDeployCash(): Promise { +async function getDeployChecklist(): Promise { const {data} = await GithubUtils.octokit.issues.listForRepo({ owner: CONST.GITHUB_OWNER, repo: CONST.APP_REPO, @@ -113,10 +113,10 @@ async function getStagingDeployCash(): Promise { throw new Error(`Found an undefined ${CONST.LABELS.STAGING_DEPLOY} issue.`); } - return getStagingDeployCashData(issue); + return getDeployChecklistData(issue); } -function getStagingDeployCashData(issue: OctokitIssueItem): StagingDeployCashData { +function getDeployChecklistData(issue: OctokitIssueItem): DeployChecklistData { try { const versionRegex = new RegExp('([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:-([0-9]+))?', 'g'); const version = (issue.body?.match(versionRegex)?.[0] ?? '').replaceAll('`', ''); @@ -126,10 +126,10 @@ function getStagingDeployCashData(issue: OctokitIssueItem): StagingDeployCashDat url: issue.url, number: GithubUtils.getIssueOrPullRequestNumberFromURL(issue.url), labels: issue.labels, - PRList: getStagingDeployCashPRList(issue), - PRListMobileExpensify: getStagingDeployCashPRListMobileExpensify(issue), - deployBlockers: getStagingDeployCashDeployBlockers(issue), - internalQAPRList: getStagingDeployCashInternalQA(issue), + PRList: getDeployChecklistPRList(issue), + PRListMobileExpensify: getDeployChecklistPRListMobileExpensify(issue), + deployBlockers: getDeployChecklistDeployBlockers(issue), + internalQAPRList: getDeployChecklistInternalQA(issue), isSentryChecked: issue.body ? /-\s\[x]\sI checked \[Sentry]/.test(issue.body) : false, isGHStatusChecked: issue.body ? /-\s\[x]\sI checked \[GitHub Status]/.test(issue.body) : false, version, @@ -141,10 +141,10 @@ function getStagingDeployCashData(issue: OctokitIssueItem): StagingDeployCashDat } /** - * Generate the issue body and assignees for a StagingDeployCash checklist. + * Generate the issue body and assignees for a deploy checklist. * Accepts PR numbers directly (not URLs) to avoid unnecessary roundtripping. */ -async function generateStagingDeployCashBodyAndAssignees({ +async function generateDeployChecklistBodyAndAssignees({ tag, PRList, PRListMobileExpensify = [], @@ -157,7 +157,7 @@ async function generateStagingDeployCashBodyAndAssignees({ isGHStatusChecked = false, previousTag = '', chronologicalSection = '', -}: StagingDeployCashParams): Promise { +}: DeployChecklistParams): Promise { const data = await GithubUtils.fetchAllPullRequests(PRList); const internalQAPRs = Array.isArray(data) ? data.filter((pr) => pr.labels.some((item) => item.name === CONST.LABELS.INTERNAL_QA)) : []; @@ -260,5 +260,5 @@ async function generateStagingDeployCashBodyAndAssignees({ return {issueBody, issueAssignees}; } -export {getStagingDeployCash, getStagingDeployCashData, generateStagingDeployCashBodyAndAssignees, parseChecklistSection}; -export type {ChecklistItem, StagingDeployCashBody, StagingDeployCashParams, StagingDeployCashData}; +export {getDeployChecklist, getDeployChecklistData, generateDeployChecklistBodyAndAssignees, parseChecklistSection}; +export type {ChecklistItem, DeployChecklistBody, DeployChecklistParams, DeployChecklistData}; diff --git a/.github/scripts/buildActions.sh b/.github/scripts/buildActions.sh index ad2156efd6fc6..37f97b8c7743a 100755 --- a/.github/scripts/buildActions.sh +++ b/.github/scripts/buildActions.sh @@ -25,7 +25,7 @@ declare -r GITHUB_ACTIONS=( "$ACTIONS_DIR/getPreviousVersion/getPreviousVersion.ts" "$ACTIONS_DIR/getPullRequestDetails/getPullRequestDetails.ts" "$ACTIONS_DIR/getPullRequestIncrementalChanges/getPullRequestIncrementalChanges.ts" - "$ACTIONS_DIR/isStagingDeployLocked/isStagingDeployLocked.ts" + "$ACTIONS_DIR/isDeployChecklistLocked/isDeployChecklistLocked.ts" "$ACTIONS_DIR/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts" "$ACTIONS_DIR/postTestBuildComment/postTestBuildComment.ts" "$ACTIONS_DIR/proposalPoliceComment/proposalPoliceComment.ts" diff --git a/.github/workflows/createDeployChecklist.yml b/.github/workflows/createDeployChecklist.yml index 742d8a40879e4..5999f0f1aeb4c 100644 --- a/.github/workflows/createDeployChecklist.yml +++ b/.github/workflows/createDeployChecklist.yml @@ -16,6 +16,6 @@ jobs: uses: ./.github/actions/composite/setupNode - name: Create or update deploy checklist - run: npx ts-node scripts/createOrUpdateStagingDeploy.ts + run: npx ts-node scripts/createOrUpdateDeployChecklist.ts env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index 2e703bd0086a4..8a0e646d07d00 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -4,7 +4,7 @@ on: issues: types: [closed] -# The updateProduction and createNewStagingDeployCash jobs are executed when a StagingDeployCash is closed. +# The updateProduction and createNewDeployChecklist jobs are executed when a deploy checklist is closed. jobs: validate: runs-on: blacksmith-2vcpu-ubuntu-2404 diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index 0f52455f6e126..4dda19f7d8700 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -50,9 +50,9 @@ jobs: with: github_token: ${{ github.token }} - - name: Check if StagingDeployCash is locked - id: isStagingDeployLocked - uses: ./.github/actions/javascript/isStagingDeployLocked + - name: Check if deploy checklist is locked + id: isDeployChecklistLocked + uses: ./.github/actions/javascript/isDeployChecklistLocked with: GITHUB_TOKEN: ${{ github.token }} @@ -60,7 +60,7 @@ jobs: id: shouldDeploy run: | if gh api /orgs/Expensify/teams/mobile-deployers/memberships/${{ github.actor }} --silent; then - echo "SHOULD_DEPLOY=${{ (!fromJSON(steps.isStagingDeployLocked.outputs.IS_LOCKED) && github.actor != 'OSBotify') }}" >> "$GITHUB_OUTPUT" + echo "SHOULD_DEPLOY=${{ (!fromJSON(steps.isDeployChecklistLocked.outputs.IS_LOCKED) && github.actor != 'OSBotify') }}" >> "$GITHUB_OUTPUT" else echo "SHOULD_DEPLOY=false" >> "$GITHUB_OUTPUT" fi diff --git a/scripts/createOrUpdateStagingDeploy.ts b/scripts/createOrUpdateDeployChecklist.ts similarity index 93% rename from scripts/createOrUpdateStagingDeploy.ts rename to scripts/createOrUpdateDeployChecklist.ts index df4d7c20024a9..2456e3cc3de52 100644 --- a/scripts/createOrUpdateStagingDeploy.ts +++ b/scripts/createOrUpdateDeployChecklist.ts @@ -4,11 +4,11 @@ import * as core from '@actions/core'; import {format} from 'date-fns/format'; import fs from 'fs'; import CONST from '@github/libs/CONST'; +import {generateDeployChecklistBodyAndAssignees, getDeployChecklistData} from '@github/libs/DeployChecklistUtils'; +import type {ChecklistItem, DeployChecklistData} from '@github/libs/DeployChecklistUtils'; import GithubUtils from '@github/libs/GithubUtils'; import GitUtils from '@github/libs/GitUtils'; import type {MergedPR, SubmoduleUpdate} from '@github/libs/GitUtils'; -import {generateStagingDeployCashBodyAndAssignees, getStagingDeployCashData} from '@github/libs/StagingDeployUtils'; -import type {ChecklistItem, StagingDeployCashData} from '@github/libs/StagingDeployUtils'; type IssuesCreateResponse = Awaited>['data']; @@ -122,8 +122,8 @@ async function buildUpdateChecklistParams({ newVersion: string; newPRNumbers: number[]; mergedMobileExpensifyPREntries: MergedPR[]; - previousChecklistData: StagingDeployCashData; - currentChecklistData: StagingDeployCashData; + previousChecklistData: DeployChecklistData; + currentChecklistData: DeployChecklistData; chronologicalSection: string; }) { const PRList = preserveCheckboxState( @@ -211,9 +211,9 @@ async function run(): Promise { const shouldCreateNewDeployChecklist = mostRecentChecklist.state !== 'open'; const previousChecklist = shouldCreateNewDeployChecklist ? mostRecentChecklist : recentDeployChecklists.at(1); if (shouldCreateNewDeployChecklist) { - console.log('Latest StagingDeployCash is closed, creating a new one.', mostRecentChecklist); + console.log('Latest deploy checklist is closed, creating a new one.', mostRecentChecklist); } else { - console.log('Latest StagingDeployCash is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); + console.log('Latest deploy checklist is open, updating it instead of creating a new one.', 'Current:', mostRecentChecklist, 'Previous:', previousChecklist); } if (!previousChecklist) { @@ -221,8 +221,8 @@ async function run(): Promise { } // Parse checklist data from the previous (and optionally current) deploy checklists - const previousChecklistData = getStagingDeployCashData(previousChecklist); - const currentChecklistData: StagingDeployCashData | undefined = shouldCreateNewDeployChecklist ? undefined : getStagingDeployCashData(mostRecentChecklist); + const previousChecklistData = getDeployChecklistData(previousChecklist); + const currentChecklistData: DeployChecklistData | undefined = shouldCreateNewDeployChecklist ? undefined : getDeployChecklistData(mostRecentChecklist); // Find PRs merged between the previous checklist's tag and the new staging tag const {mergedPRs: mergedPREntries, submoduleUpdates} = await GitUtils.getMergedPRsDeployedBetween(previousChecklistData.tag, newStagingTag, CONST.APP_REPO); @@ -301,7 +301,7 @@ async function run(): Promise { throw new Error('Expected current checklist data for update but it was undefined'); } - const {issueBody: checklistBody, issueAssignees: checklistAssignees} = await generateStagingDeployCashBodyAndAssignees(checklistParams); + const {issueBody: checklistBody, issueAssignees: checklistAssignees} = await generateDeployChecklistBodyAndAssignees(checklistParams); // Finally, create or update the checklist const defaultPayload = { @@ -317,7 +317,7 @@ async function run(): Promise { labels: [CONST.LABELS.STAGING_DEPLOY, CONST.LABELS.LOCK_DEPLOY], assignees: [CONST.APPLAUSE_BOT as string].concat(checklistAssignees), }); - console.log(`Successfully created new StagingDeployCash! 🎉 ${newChecklist.html_url}`); + console.log(`Successfully created new deploy checklist! 🎉 ${newChecklist.html_url}`); return newChecklist; } @@ -325,7 +325,7 @@ async function run(): Promise { ...defaultPayload, issue_number: currentChecklistData?.number ?? 0, }); - console.log(`Successfully updated StagingDeployCash! 🎉 ${updatedChecklist.html_url}`); + console.log(`Successfully updated deploy checklist! 🎉 ${updatedChecklist.html_url}`); return updatedChecklist; } catch (err: unknown) { console.error('An unknown error occurred!', err); diff --git a/tests/unit/StagingDeployUtilsTest.ts b/tests/unit/DeployChecklistUtilsTest.ts similarity index 90% rename from tests/unit/StagingDeployUtilsTest.ts rename to tests/unit/DeployChecklistUtilsTest.ts index 49a7ddcf02eb6..036a525c8fb39 100644 --- a/tests/unit/StagingDeployUtilsTest.ts +++ b/tests/unit/DeployChecklistUtilsTest.ts @@ -4,9 +4,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import * as core from '@actions/core'; import type {Writable} from 'type-fest'; +import {generateDeployChecklistBodyAndAssignees, getDeployChecklist} from '@github/libs/DeployChecklistUtils'; import type {InternalOctokit, ListForRepoMethod} from '@github/libs/GithubUtils'; import GithubUtils from '@github/libs/GithubUtils'; -import {generateStagingDeployCashBodyAndAssignees, getStagingDeployCash} from '@github/libs/StagingDeployUtils'; const mockGetInput = jest.fn(); const mockListIssues = jest.fn(); @@ -66,8 +66,8 @@ afterEach(() => { mockListIssues.mockClear(); }); -describe('StagingDeployUtils', () => { - describe('getStagingDeployCash', () => { +describe('DeployChecklistUtils', () => { + describe('getDeployChecklist', () => { const baseIssue: Issue = { url: 'https://api.github.com/repos/Andrew-Test-Org/Public-Test-Repo/issues/29', title: 'Andrew Test Issue', @@ -90,7 +90,7 @@ describe('StagingDeployUtils', () => { // eslint-disable-next-line max-len issueWithDeployBlockers.body += `\r\n**Deploy Blockers:**\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/1\r\n- [x] https://github.com/${process.env.GITHUB_REPOSITORY}/issues/2\r\n- [ ] https://github.com/${process.env.GITHUB_REPOSITORY}/pull/1234\r\n`; - const baseExpectedResponse: Partial>> = { + const baseExpectedResponse: Partial>> = { PRList: [ { url: `https://github.com/${process.env.GITHUB_REPOSITORY}/pull/21`, @@ -157,24 +157,24 @@ describe('StagingDeployUtils', () => { body: `**Release Version:** \`1.0.1-47\`\r\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\r\n\r\ncc @Expensify/applauseleads\n`, }; - const bareExpectedResponse: Partial>> = { + const bareExpectedResponse: Partial>> = { ...baseExpectedResponse, PRList: [], PRListMobileExpensify: [], }; GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [bareIssue]}) as unknown as ListForRepoMethod; - return getStagingDeployCash().then((data) => expect(data).toStrictEqual(bareExpectedResponse)); + return getDeployChecklist().then((data) => expect(data).toStrictEqual(bareExpectedResponse)); }); test('Test finding an open issue successfully', () => { GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [baseIssue]}) as unknown as ListForRepoMethod; - return getStagingDeployCash().then((data) => expect(data).toStrictEqual(baseExpectedResponse)); + return getDeployChecklist().then((data) => expect(data).toStrictEqual(baseExpectedResponse)); }); test('Test finding an open issue successfully and parsing with deploy blockers', () => { GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [issueWithDeployBlockers]}) as unknown as ListForRepoMethod; - return getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); + return getDeployChecklist().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); }); test('Test finding an open issue successfully and parsing with blockers w/o carriage returns', () => { @@ -184,7 +184,7 @@ describe('StagingDeployUtils', () => { GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({ data: [modifiedIssueWithDeployBlockers], }) as unknown as ListForRepoMethod; - return getStagingDeployCash().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); + return getDeployChecklist().then((data) => expect(data).toStrictEqual(expectedResponseWithDeployBlockers)); }); test('Test finding an open issue without a body', () => { @@ -192,21 +192,21 @@ describe('StagingDeployUtils', () => { noBodyIssue.body = ''; GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [noBodyIssue]}) as unknown as ListForRepoMethod; - return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue with correct data.'))); + return getDeployChecklist().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue with correct data.'))); }); test('Test finding more than one issue', () => { GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: [{a: 1}, {b: 2}]}) as unknown as ListForRepoMethod; - return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Found more than one StagingDeployCash issue.'))); + return getDeployChecklist().catch((e) => expect(e).toEqual(new Error('Found more than one StagingDeployCash issue.'))); }); test('Test finding no issues', () => { GithubUtils.octokit.issues.listForRepo = jest.fn().mockResolvedValue({data: []}) as unknown as ListForRepoMethod; - return getStagingDeployCash().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue.'))); + return getDeployChecklist().catch((e) => expect(e).toEqual(new Error('Unable to find StagingDeployCash issue.'))); }); }); - describe('generateStagingDeployCashBody', () => { + describe('generateDeployChecklistBody', () => { const mockPRs = [ { number: 1, @@ -337,7 +337,7 @@ describe('StagingDeployUtils', () => { `${lineBreak}`; test('Test no verified PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; expect(issue.issueBody).toBe( `${expectedOutputWithMobileExpensify}` + @@ -360,19 +360,19 @@ describe('StagingDeployUtils', () => { }); test('Test Mobile-Expensify compare link with Mobile-Expensify PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify}); expect(issue.issueBody).toContain('**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging'); expect(issue.issueBody).toContain('**Mobile-Expensify PRs:**'); }); test('Test no Mobile-Expensify compare link without Mobile-Expensify PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify: []}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, PRListMobileExpensify: []}); expect(issue.issueBody).not.toContain('**Mobile-Expensify Changes:**'); expect(issue.issueBody).not.toContain('**Mobile-Expensify PRs:**'); }); test('Test some verified PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: [2]}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: [2]}); expect(issue.issueBody).toBe( `${baseExpectedOutput}` + `${openCheckbox}${prURL(1)}` + @@ -390,7 +390,7 @@ describe('StagingDeployUtils', () => { }); test('Test all verified PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList}); expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${deployerVerificationsHeader}` + @@ -403,7 +403,7 @@ describe('StagingDeployUtils', () => { }); test('Test no resolved deploy blockers', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList, deployBlockers: baseDeployBlockerList}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: basePRList, verifiedPRList: basePRList, deployBlockers: baseDeployBlockerList}); expect(issue.issueBody).toBe( `${allVerifiedExpectedOutput}` + `${deployBlockerHeader}` + @@ -419,7 +419,7 @@ describe('StagingDeployUtils', () => { }); test('Test some resolved deploy blockers', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({ + const issue = await generateDeployChecklistBodyAndAssignees({ tag, PRList: basePRList, verifiedPRList: basePRList, @@ -441,7 +441,7 @@ describe('StagingDeployUtils', () => { }); test('Test all resolved deploy blockers', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({ + const issue = await generateDeployChecklistBodyAndAssignees({ tag, PRList: basePRList, verifiedPRList: basePRList, @@ -468,7 +468,7 @@ describe('StagingDeployUtils', () => { }); test('Test internalQA PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], PRListMobileExpensify}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], PRListMobileExpensify}); const expectedOutputWithMobileExpensify = `**Release Version:** \`${tag}\`\n**Compare Changes:** https://github.com/${process.env.GITHUB_REPOSITORY}/compare/production...staging\n**Mobile-Expensify Changes:** https://github.com/Expensify/Mobile-Expensify/compare/production...staging\n\n**This release contains changes from the following pull requests:**\n`; expect(issue.issueBody).toBe( `${expectedOutputWithMobileExpensify}` + @@ -494,7 +494,7 @@ describe('StagingDeployUtils', () => { }); test('Test some verified internalQA PRs', async () => { - const issue = await generateStagingDeployCashBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], resolvedInternalQAPRs: [6]}); + const issue = await generateDeployChecklistBodyAndAssignees({tag, PRList: [...basePRList, ...internalQAPRList], resolvedInternalQAPRs: [6]}); expect(issue.issueBody).toBe( `${baseExpectedOutput}` + `${openCheckbox}${prURL(1)}` + diff --git a/tests/unit/createOrUpdateStagingDeployTest.ts b/tests/unit/createOrUpdateDeployChecklistTest.ts similarity index 95% rename from tests/unit/createOrUpdateStagingDeployTest.ts rename to tests/unit/createOrUpdateDeployChecklistTest.ts index 0b9ca037c7881..350606cd347cd 100644 --- a/tests/unit/createOrUpdateStagingDeployTest.ts +++ b/tests/unit/createOrUpdateDeployChecklistTest.ts @@ -3,11 +3,11 @@ import * as fns from 'date-fns'; import {vol} from 'memfs'; import path from 'path'; import CONST from '@github/libs/CONST'; +import * as DeployChecklistUtils from '@github/libs/DeployChecklistUtils'; import type {InternalOctokit} from '@github/libs/GithubUtils'; import GithubUtils from '@github/libs/GithubUtils'; import GitUtils from '@github/libs/GitUtils'; -import * as StagingDeployUtils from '@github/libs/StagingDeployUtils'; -import run from '@scripts/createOrUpdateStagingDeploy'; +import run from '@scripts/createOrUpdateDeployChecklist'; /** * @jest-environment node @@ -65,7 +65,7 @@ beforeAll(() => { }, pulls: { // Static mock for pulls.list (only used to filter out automated PRs, and that functionality is covered - // in the test for GithubUtils.generateStagingDeployCashBody + // in the test for GithubUtils.generateDeployChecklistBody list: jest.fn().mockResolvedValue([]), }, }, @@ -161,10 +161,10 @@ const deployBlockerHeader = '**Deploy Blockers:**'; const lineBreak = '\n'; const lineBreakDouble = '\n\n'; -describe('createOrUpdateStagingDeployCash', () => { - const closedStagingDeployCash = { +describe('createOrUpdateDeployChecklist', () => { + const closedDeployChecklist = { url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/issues/28`, - title: 'Test StagingDeployCash', + title: 'Test Deploy Checklist', number: 28, labels: [LABELS.STAGING_DEPLOY_CASH], html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, @@ -255,7 +255,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); @@ -306,7 +306,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); @@ -336,9 +336,9 @@ describe('createOrUpdateStagingDeployCash', () => { }); describe('updates existing issue when there is one open', () => { - const openStagingDeployCashBefore = { + const openDeployChecklistBefore = { url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/issues/29`, - title: 'Test StagingDeployCash', + title: 'Test Deploy Checklist', number: 29, labels: [LABELS.STAGING_DEPLOY_CASH], // eslint-disable-next-line max-len @@ -386,7 +386,7 @@ describe('createOrUpdateStagingDeployCash', () => { [PATH_TO_PACKAGE_JSON]: JSON.stringify({version: '1.0.2-2'}), }); - // New pull requests to add to open StagingDeployCash + // New pull requests to add to the open deploy checklist const newPullRequests = [9, 10]; mockGetMergedPRsDeployedBetween.mockImplementation(async (fromRef, toRef, repositoryName) => { if (fromRef === '1.0.1-0-staging' && toRef === '1.0.2-2-staging') { @@ -400,7 +400,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [openStagingDeployCashBefore, closedStagingDeployCash]}); + return Promise.resolve({data: [openDeployChecklistBefore, closedDeployChecklist]}); } if (args.labels === CONST.LABELS.DEPLOY_BLOCKER) { @@ -430,9 +430,9 @@ describe('createOrUpdateStagingDeployCash', () => { expect(result).toStrictEqual({ owner: CONST.GITHUB_OWNER, repo: CONST.APP_REPO, - issue_number: openStagingDeployCashBefore.number, + issue_number: openDeployChecklistBefore.number, // eslint-disable-next-line max-len, @typescript-eslint/naming-convention - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openStagingDeployCashBefore.number}`, + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openDeployChecklistBefore.number}`, // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.2-2')}` + @@ -479,7 +479,7 @@ describe('createOrUpdateStagingDeployCash', () => { }); mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [openStagingDeployCashBefore, closedStagingDeployCash]}); + return Promise.resolve({data: [openDeployChecklistBefore, closedDeployChecklist]}); } if (args.labels === CONST.LABELS.DEPLOY_BLOCKER) { @@ -510,9 +510,9 @@ describe('createOrUpdateStagingDeployCash', () => { expect(result).toStrictEqual({ owner: CONST.GITHUB_OWNER, repo: CONST.APP_REPO, - issue_number: openStagingDeployCashBefore.number, + issue_number: openDeployChecklistBefore.number, // eslint-disable-next-line max-len, @typescript-eslint/naming-convention - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openStagingDeployCashBefore.number}`, + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openDeployChecklistBefore.number}`, // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.2-1')}` + @@ -556,7 +556,7 @@ describe('createOrUpdateStagingDeployCash', () => { }); mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [openStagingDeployCashBefore, closedStagingDeployCash]}); + return Promise.resolve({data: [openDeployChecklistBefore, closedDeployChecklist]}); } if (args.labels === CONST.LABELS.DEPLOY_BLOCKER) { @@ -570,9 +570,9 @@ describe('createOrUpdateStagingDeployCash', () => { expect(result).toStrictEqual({ owner: CONST.GITHUB_OWNER, repo: CONST.APP_REPO, - issue_number: openStagingDeployCashBefore.number, + issue_number: openDeployChecklistBefore.number, // eslint-disable-next-line max-len, @typescript-eslint/naming-convention - html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openStagingDeployCashBefore.number}`, + html_url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/${openDeployChecklistBefore.number}`, // eslint-disable-next-line max-len body: `${baseExpectedOutput('1.0.2-1', false)}` + @@ -614,8 +614,8 @@ describe('createOrUpdateStagingDeployCash', () => { }); // Mock previous checklist containing PRs 6,8 - const mockGetStagingDeployCashData = jest.spyOn(StagingDeployUtils, 'getStagingDeployCashData'); - mockGetStagingDeployCashData.mockImplementation(() => ({ + const mockGetDeployChecklistData = jest.spyOn(DeployChecklistUtils, 'getDeployChecklistData'); + mockGetDeployChecklistData.mockImplementation(() => ({ title: 'Previous Checklist', url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, number: 29, @@ -661,7 +661,7 @@ describe('createOrUpdateStagingDeployCash', () => { expect(result?.body).not.toContain('https://github.com/Expensify/App/pull/6'); expect(result?.body).not.toContain('https://github.com/Expensify/App/pull/8'); - mockGetStagingDeployCashData.mockRestore(); + mockGetDeployChecklistData.mockRestore(); }); test('filters out PRs when no Mobile-Expensify PRs exist', async () => { @@ -683,8 +683,8 @@ describe('createOrUpdateStagingDeployCash', () => { }); // Mock previous checklist containing PRs 6,8 but no Mobile-Expensify PRs - const mockGetStagingDeployCashData = jest.spyOn(StagingDeployUtils, 'getStagingDeployCashData'); - mockGetStagingDeployCashData.mockImplementation(() => ({ + const mockGetDeployChecklistData = jest.spyOn(DeployChecklistUtils, 'getDeployChecklistData'); + mockGetDeployChecklistData.mockImplementation(() => ({ title: 'Previous Checklist', url: `https://github.com/${process.env.GITHUB_REPOSITORY}/issues/29`, number: 29, @@ -730,7 +730,7 @@ describe('createOrUpdateStagingDeployCash', () => { expect(result?.body).not.toContain('**Mobile-Expensify PRs:**'); expect(result?.body).not.toContain('Mobile-Expensify/pull/'); - mockGetStagingDeployCashData.mockRestore(); + mockGetDeployChecklistData.mockRestore(); }); }); @@ -779,7 +779,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); }); @@ -825,7 +825,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); }); @@ -876,9 +876,9 @@ describe('createOrUpdateStagingDeployCash', () => { mockGetWorkflowRunURLForCommit.mockImplementation(async () => workflowRunURL); - const openStagingDeployCashWithSubmodule = { + const openDeployChecklistWithSubmodule = { url: `https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/issues/29`, - title: 'Test StagingDeployCash', + title: 'Test Deploy Checklist', number: 29, labels: [LABELS.STAGING_DEPLOY_CASH], body: @@ -896,7 +896,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [openStagingDeployCashWithSubmodule, closedStagingDeployCash]}); + return Promise.resolve({data: [openDeployChecklistWithSubmodule, closedDeployChecklist]}); } if (args.labels === CONST.LABELS.DEPLOY_BLOCKER) { return Promise.resolve({data: []}); @@ -972,7 +972,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); }); @@ -1027,7 +1027,7 @@ describe('createOrUpdateStagingDeployCash', () => { mockListIssues.mockImplementation((args: Arguments) => { if (args.labels === CONST.LABELS.STAGING_DEPLOY) { - return Promise.resolve({data: [closedStagingDeployCash]}); + return Promise.resolve({data: [closedDeployChecklist]}); } return Promise.resolve({data: []}); }); diff --git a/tests/unit/isStagingDeployLockedTest.ts b/tests/unit/isDeployChecklistLockedTest.ts similarity index 60% rename from tests/unit/isStagingDeployLockedTest.ts rename to tests/unit/isDeployChecklistLockedTest.ts index 8a6aea7b912e8..e00c58e25e29f 100644 --- a/tests/unit/isStagingDeployLockedTest.ts +++ b/tests/unit/isDeployChecklistLockedTest.ts @@ -2,10 +2,10 @@ * @jest-environment node */ import * as core from '@actions/core'; -import run from '../../.github/actions/javascript/isStagingDeployLocked/isStagingDeployLocked'; -import * as StagingDeployUtils from '../../.github/libs/StagingDeployUtils'; +import run from '../../.github/actions/javascript/isDeployChecklistLocked/isDeployChecklistLocked'; +import * as DeployChecklistUtils from '../../.github/libs/DeployChecklistUtils'; -jest.mock('../../.github/libs/StagingDeployUtils'); +jest.mock('../../.github/libs/DeployChecklistUtils'); beforeAll(() => { process.env.INPUT_GITHUB_TOKEN = 'fake_token'; @@ -19,13 +19,13 @@ afterAll(() => { delete process.env.INPUT_GITHUB_TOKEN; }); -describe('isStagingDeployLockedTest', () => { +describe('isDeployChecklistLockedTest', () => { describe('GitHub action run function', () => { test('Test returning empty result', () => { - (StagingDeployUtils.getStagingDeployCash as jest.Mock).mockResolvedValue({}); + (DeployChecklistUtils.getDeployChecklist as jest.Mock).mockResolvedValue({}); const setOutputMock = jest.spyOn(core, 'setOutput'); - const isStagingDeployLocked = run(); - return isStagingDeployLocked.then(() => { + const isDeployChecklistLocked = run(); + return isDeployChecklistLocked.then(() => { expect(setOutputMock).toHaveBeenCalledWith('IS_LOCKED', false); }); }); @@ -35,10 +35,10 @@ describe('isStagingDeployLockedTest', () => { labels: [{name: '🔐 LockCashDeploys 🔐'}], }; - (StagingDeployUtils.getStagingDeployCash as jest.Mock).mockResolvedValue(mockData); + (DeployChecklistUtils.getDeployChecklist as jest.Mock).mockResolvedValue(mockData); const setOutputMock = jest.spyOn(core, 'setOutput'); - const isStagingDeployLocked = run(); - return isStagingDeployLocked.then(() => { + const isDeployChecklistLocked = run(); + return isDeployChecklistLocked.then(() => { expect(setOutputMock).toHaveBeenCalledWith('IS_LOCKED', true); }); });