import { limitBrowse } from "../limits"; import { HttpError } from "../errors"; import { assertVerifiedForWrites } from "../verified-writes"; import { handleScratchApi } from "../sites/scratch"; import { getSiteUsage } from "../sites/usage"; import { createAgentApi, createAgentTokenApi, deleteAgentApi, listAgentsApi, mcpUsageApi } from "./agents"; import { createEditorSession } from "./editor"; import { apiContext } from "./http"; import type { ApiContext } from "./http"; import { createPull, getPull, listPulls, mergePull, updatePullBranch } from "./pulls"; import { applyPullAiFix, getPullAiFix, startPullAiFix } from "./mergefix"; import { commitSuggestions, createDraftPull, createStatus, getPullCodeOwners, getPullConflicts, getPullStatus, getPullTemplate, listPullLinkedIssues, listRequestedReviewers, listReviews, listStatuses, resolvePullConflicts, setPullLinkedIssue, } from "./pull-review"; import { createRepo, deleteRepo, forkRepo, getCollaborator, getRepo, importRepo, listRepos, putCollaborator, updateRepo, getTopics, getLanguages, replaceTopics, } from "./repos"; import { approveRunApi, cancelRunApi, createOwnerRegistrationToken, createRegistrationToken, deleteOwnerRunnerApi, deleteRunnerApi, listOwnerRunnersApi, listRunnersApi, listRunsApi, putRunnerPolicy, } from "./runners"; import { createBranchRule, createProtection, deleteBranchRule, listBranchRules, listProtections, putContents, setPushProtection, updateBranchRule, } from "./settings"; import { createReview, dequeuePull, disableAutoMerge, dismissPullReview, enableAutoMerge, enqueuePull, patchPull, replyToComment, rerequestReviewer, resolveThread, patchReviewComment, deleteReviewCommentApi, setRequestedReviewers, } from "./pull-writes"; import { createLabel, createRepoDeployToken, createToken, listDeployTokens, listLabels, putCodeOwnerReview, removeDeployToken, removeLabel, updateLabel, } from "./admin-writes"; import { deleteOrg } from "./org-delete"; import { getLfsUsage, getUser, listTokens, revokeToken } from "./user"; import { getOrgUsage, getUserUsage } from "./usage"; import { bypassPushBlockApi, getCodeAlertApi, getCommitSignaturesApi, getDependenciesApi, getPullCodeAlertsApi, getPushBlockApi, getSecurityOverviewApi, listCodeAlertsApi, listSecretAlertsApi, putSecuritySettingsApi, updateCodeAlertApi, updateSecretAlertApi, } from "./security"; import { commentAdvisoryApi, createAdvisoryApi, createSigningKeyApi, deleteSigningKeyApi, getAdvisoryApi, listAdvisoriesApi, listSigningKeysApi, setAdvisoryCollaboratorApi, setAdvisoryStateApi, updateAdvisoryApi, } from "./security-advisories"; import { getCredential, getRepoPermissions } from "./credential"; import { createComment, createIssue, getIssue, listComments, listIssues, updateIssue } from "./issues"; import { createBranchApi, deleteBranchApi, listBranchesApi, listTagsApi, transferRepoApi } from "./git"; import { acceptOrgInvitationApi, createOrgApi, declineOrgInvitationApi, deleteCollaborator, deleteOrgMember, getOrgApi, listMyOrgInvitationsApi, listOrgInvitationsApi, listOrgMembers, putOrgMember, } from "./orgs"; import { checkSiteDomainApi, deleteSiteApi, deleteSiteVariableApi, deploySiteApi, getSiteApi, putSiteAccessApi, putSiteApi, putSiteDomainApi, putSiteVariableApi, rollbackSiteApi, } from "./hosting"; import { createHook, deleteHook, getLfsQuota, getNotificationSettingsApi, listHooks, listNotificationsApi, markNotificationsApi, pingHook, putNotificationSettingsApi, redeliverHook, } from "./platform"; import { createWebhookApi, deleteWebhookApi, listWebhookDeliveriesApi, listWebhooksApi, redeliverWebhookApi, } from "./webhooks"; import { deleteCacheApi, deleteSecretApi, deleteVariableApi, dispatchWorkflowApi, getRunApi, listCachesApi, listDeploymentsApi, listSecretsApi, listStatusesApi, listVariablesApi, putSecretApi, putVariableApi, rerunRunApi, reviewDeploymentApi, } from "./actions"; export const API_V1_PREFIX = "/api/v1/"; /** Anonymous scratch deploys; listed as token-only in `src/server.ts` because it never reads cookies. */ export const SCRATCH_API_PATH = "/scratch"; type Handler = (ctx: ApiContext, params: string[]) => Response | Promise; /** `selfService` writes stay open to an unverified account (AUTH-B1); every other write needs a verified email. */ type Route = { method: string; pattern: RegExp; handler: Handler; selfService: boolean }; const SEG = "([^/]+)"; const REPO = `/repos/${SEG}/${SEG}`; const PULL = `${REPO}/pulls/(\\d+)`; function route(method: string, path: string, handler: Handler): Route { return { method, pattern: new RegExp(`^${path}$`), handler, selfService: false }; } function selfService(method: string, path: string, handler: Handler): Route { return { ...route(method, path, handler), selfService: true }; } function pullNumber(raw: string): number { const number = Number(raw); if (!Number.isSafeInteger(number) || number <= 0) throw new HttpError(404, "Pull request not found"); return number; } function issueNumber(raw: string): number { const number = Number(raw); if (!Number.isSafeInteger(number) || number <= 0) throw new HttpError(404, "Issue not found"); return number; } const ROUTES: Route[] = [ route("GET", "/user", (ctx) => getUser(ctx)), route("GET", "/user/lfs-usage", (ctx) => getLfsUsage(ctx)), route("GET", "/user/usage", (ctx) => getUserUsage(ctx)), route("GET", `/orgs/${SEG}/usage`, (ctx, [org]) => getOrgUsage(ctx, org)), route("GET", "/user/site-usage", (ctx) => getSiteUsage(ctx.viewer)), route("GET", "/user/tokens", (ctx) => listTokens(ctx)), route("POST", "/user/tokens", (ctx) => createToken(ctx)), selfService("DELETE", `/orgs/${SEG}`, (ctx, [org]) => deleteOrg(ctx, org)), route("GET", "/user/agents", (ctx) => listAgentsApi(ctx)), route("GET", "/user/mcp-usage", (ctx) => mcpUsageApi(ctx)), route("POST", "/user/agents", (ctx) => createAgentApi(ctx)), selfService("DELETE", `/user/agents/${SEG}`, (ctx, [id]) => deleteAgentApi(ctx, id)), route("POST", `/user/agents/${SEG}/tokens`, (ctx, [id]) => createAgentTokenApi(ctx, id)), selfService("DELETE", `/user/tokens/${SEG}`, (ctx, [id]) => revokeToken(ctx, id)), route("GET", "/repos", (ctx) => listRepos(ctx)), route("POST", "/repos", (ctx) => createRepo(ctx)), route("GET", REPO, (ctx, [o, n]) => getRepo(ctx, o, n)), route("PATCH", REPO, (ctx, [o, n]) => updateRepo(ctx, o, n)), selfService("DELETE", REPO, (ctx, [o, n]) => deleteRepo(ctx, o, n)), route("POST", `${REPO}/forks`, (ctx, [o, n]) => forkRepo(ctx, o, n)), route("GET", `${REPO}/topics`, (ctx, [o, n]) => getTopics(ctx, o, n)), route("GET", `${REPO}/languages`, (ctx, [o, n]) => getLanguages(ctx, o, n)), route("PUT", `${REPO}/topics`, (ctx, [o, n]) => replaceTopics(ctx, o, n)), route("GET", `${REPO}/collaborators/${SEG}`, (ctx, [o, n, u]) => getCollaborator(ctx, o, n, u)), route("PUT", `${REPO}/collaborators/${SEG}`, (ctx, [o, n, u]) => putCollaborator(ctx, o, n, u)), route("GET", `${REPO}/protections`, (ctx, [o, n]) => listProtections(ctx, o, n)), route("POST", `${REPO}/protections`, (ctx, [o, n]) => createProtection(ctx, o, n)), route("PUT", `${REPO}/settings/push-protection`, (ctx, [o, n]) => setPushProtection(ctx, o, n)), route("PUT", `${REPO}/contents/(.+)`, (ctx, [o, n, p]) => putContents(ctx, o, n, p)), route("GET", `${REPO}/pulls`, (ctx, [o, n]) => listPulls(ctx, o, n)), route("POST", `${REPO}/pulls`, (ctx, [o, n]) => createPull(ctx, o, n)), route("GET", `${REPO}/pulls/(\\d+)`, (ctx, [o, n, num]) => getPull(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/pulls/(\\d+)/merge`, (ctx, [o, n, num]) => mergePull(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/pulls/(\\d+)/update-branch`, (ctx, [o, n, num]) => updatePullBranch(ctx, o, n, pullNumber(num)), ), route("POST", `${REPO}/pulls/drafts`, (ctx, [o, n]) => createDraftPull(ctx, o, n)), route("GET", `${REPO}/pull-template`, (ctx, [o, n]) => getPullTemplate(ctx, o, n)), route("GET", `${PULL}/status`, (ctx, [o, n, num]) => getPullStatus(ctx, o, n, pullNumber(num))), route("GET", `${PULL}/conflicts`, (ctx, [o, n, num]) => getPullConflicts(ctx, o, n, pullNumber(num))), route("POST", `${PULL}/conflicts`, (ctx, [o, n, num]) => resolvePullConflicts(ctx, o, n, pullNumber(num))), route("POST", `${PULL}/suggestions`, (ctx, [o, n, num]) => commitSuggestions(ctx, o, n, pullNumber(num))), route("GET", `${PULL}/reviews`, (ctx, [o, n, num]) => listReviews(ctx, o, n, pullNumber(num))), route("GET", `${PULL}/requested-reviewers`, (ctx, [o, n, num]) => listRequestedReviewers(ctx, o, n, pullNumber(num))), route("GET", `${PULL}/codeowners`, (ctx, [o, n, num]) => getPullCodeOwners(ctx, o, n, pullNumber(num))), route("GET", `${PULL}/linked-issues`, (ctx, [o, n, num]) => listPullLinkedIssues(ctx, o, n, pullNumber(num))), route("PUT", `${PULL}/linked-issues/(\\d+)`, (ctx, [o, n, num, issue]) => setPullLinkedIssue(ctx, o, n, pullNumber(num), pullNumber(issue), true), ), route("DELETE", `${PULL}/linked-issues/(\\d+)`, (ctx, [o, n, num, issue]) => setPullLinkedIssue(ctx, o, n, pullNumber(num), pullNumber(issue), false), ), route("GET", `${REPO}/statuses/${SEG}`, (ctx, [o, n, sha]) => listStatuses(ctx, o, n, sha)), route("POST", `${REPO}/statuses/${SEG}`, (ctx, [o, n, sha]) => createStatus(ctx, o, n, sha)), route("POST", `${REPO}/pulls/(\\d+)/ai-fix`, (ctx, [o, n, num]) => startPullAiFix(ctx, o, n, pullNumber(num))), route("GET", `${REPO}/pulls/(\\d+)/ai-fix`, (ctx, [o, n, num]) => getPullAiFix(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/pulls/(\\d+)/ai-fix/apply`, (ctx, [o, n, num]) => applyPullAiFix(ctx, o, n, pullNumber(num))), route("PATCH", `${PULL}`, (ctx, [o, n, num]) => patchPull(ctx, o, n, pullNumber(num))), route("POST", `${PULL}/reviews`, (ctx, [o, n, num]) => createReview(ctx, o, n, pullNumber(num))), route("PUT", `${PULL}/reviews/${SEG}/dismissals`, (ctx, [o, n, num, id]) => dismissPullReview(ctx, o, n, pullNumber(num), id), ), route("POST", `${PULL}/comments/${SEG}/replies`, (ctx, [o, n, num, id]) => replyToComment(ctx, o, n, pullNumber(num), id), ), route("PUT", `${PULL}/threads/${SEG}`, (ctx, [o, n, num, id]) => resolveThread(ctx, o, n, pullNumber(num), id)), route("PATCH", `${PULL}/comments/${SEG}`, (ctx, [o, n, num, id]) => patchReviewComment(ctx, o, n, pullNumber(num), id), ), route("DELETE", `${PULL}/comments/${SEG}`, (ctx, [o, n, num, id]) => deleteReviewCommentApi(ctx, o, n, pullNumber(num), id), ), route("POST", `${PULL}/requested_reviewers`, (ctx, [o, n, num]) => setRequestedReviewers(ctx, o, n, pullNumber(num))), route("POST", `${PULL}/requested_reviewers/rerequest`, (ctx, [o, n, num]) => rerequestReviewer(ctx, o, n, pullNumber(num)), ), route("PUT", `${PULL}/auto_merge`, (ctx, [o, n, num]) => enableAutoMerge(ctx, o, n, pullNumber(num))), route("DELETE", `${PULL}/auto_merge`, (ctx, [o, n, num]) => disableAutoMerge(ctx, o, n, pullNumber(num))), route("PUT", `${PULL}/merge_queue`, (ctx, [o, n, num]) => enqueuePull(ctx, o, n, pullNumber(num))), route("DELETE", `${PULL}/merge_queue`, (ctx, [o, n, num]) => dequeuePull(ctx, o, n, pullNumber(num))), route("GET", `${REPO}/branch-protections`, (ctx, [o, n]) => listBranchRules(ctx, o, n)), route("POST", `${REPO}/branch-protections`, (ctx, [o, n]) => createBranchRule(ctx, o, n)), route("PUT", `${REPO}/branch-protections/${SEG}`, (ctx, [o, n, id]) => updateBranchRule(ctx, o, n, id)), route("DELETE", `${REPO}/branch-protections/${SEG}`, (ctx, [o, n, id]) => deleteBranchRule(ctx, o, n, id)), route("PUT", `${REPO}/branch-protections/${SEG}/code-owner-review`, (ctx, [o, n, id]) => putCodeOwnerReview(ctx, o, n, id), ), route("GET", `${REPO}/labels`, (ctx, [o, n]) => listLabels(ctx, o, n)), route("POST", `${REPO}/labels`, (ctx, [o, n]) => createLabel(ctx, o, n)), route("PATCH", `${REPO}/labels/${SEG}`, (ctx, [o, n, id]) => updateLabel(ctx, o, n, id)), route("DELETE", `${REPO}/labels/${SEG}`, (ctx, [o, n, id]) => removeLabel(ctx, o, n, id)), route("GET", `${REPO}/deploy-tokens`, (ctx, [o, n]) => listDeployTokens(ctx, o, n)), route("POST", `${REPO}/deploy-tokens`, (ctx, [o, n]) => createRepoDeployToken(ctx, o, n)), route("DELETE", `${REPO}/deploy-tokens/${SEG}`, (ctx, [o, n, id]) => removeDeployToken(ctx, o, n, id)), route("POST", `${REPO}/import`, (ctx, [o, n]) => importRepo(ctx, o, n)), route("POST", `${REPO}/actions/runners/registration-token`, (ctx, [o, n]) => createRegistrationToken(ctx, o, n)), route("GET", `${REPO}/actions/runners`, (ctx, [o, n]) => listRunnersApi(ctx, o, n)), route("PUT", `${REPO}/actions/runners/policy`, (ctx, [o, n]) => putRunnerPolicy(ctx, o, n)), route("DELETE", `${REPO}/actions/runners/([a-z0-9]+)`, (ctx, [o, n, id]) => deleteRunnerApi(ctx, o, n, id)), route("GET", `${REPO}/actions/runs`, (ctx, [o, n]) => listRunsApi(ctx, o, n)), route("POST", `/orgs/${SEG}/actions/runners/registration-token`, (ctx, [o]) => createOwnerRegistrationToken(ctx, o)), route("GET", `/orgs/${SEG}/actions/runners`, (ctx, [o]) => listOwnerRunnersApi(ctx, o)), route("DELETE", `/orgs/${SEG}/actions/runners/([a-z0-9]+)`, (ctx, [o, id]) => deleteOwnerRunnerApi(ctx, o, id)), route("POST", `${REPO}/actions/runs/(\\d+)/cancel`, (ctx, [o, n, num]) => cancelRunApi(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/actions/runs/(\\d+)/approve`, (ctx, [o, n, num]) => approveRunApi(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/editor-sessions`, (ctx, [o, n]) => createEditorSession(ctx, o, n)), route("GET", "/user/signing-keys", (ctx) => listSigningKeysApi(ctx)), selfService("POST", "/user/signing-keys", (ctx) => createSigningKeyApi(ctx)), selfService("DELETE", `/user/signing-keys/${SEG}`, (ctx, [id]) => deleteSigningKeyApi(ctx, id)), route("GET", `${REPO}/security`, (ctx, [o, n]) => getSecurityOverviewApi(ctx, o, n)), route("PUT", `${REPO}/security/settings`, (ctx, [o, n]) => putSecuritySettingsApi(ctx, o, n)), route("GET", `${REPO}/secret-scanning/alerts`, (ctx, [o, n]) => listSecretAlertsApi(ctx, o, n)), route("PATCH", `${REPO}/secret-scanning/alerts/(\\d+)`, (ctx, [o, n, num]) => updateSecretAlertApi(ctx, o, n, pullNumber(num)), ), route("GET", `${REPO}/secret-scanning/push-blocks/${SEG}`, (ctx, [o, n, id]) => getPushBlockApi(ctx, o, n, id)), route("POST", `${REPO}/secret-scanning/push-blocks/${SEG}/bypass`, (ctx, [o, n, id]) => bypassPushBlockApi(ctx, o, n, id), ), route("GET", `${REPO}/code-scanning/alerts`, (ctx, [o, n]) => listCodeAlertsApi(ctx, o, n)), route("GET", `${REPO}/code-scanning/alerts/(\\d+)`, (ctx, [o, n, num]) => getCodeAlertApi(ctx, o, n, pullNumber(num)), ), route("PATCH", `${REPO}/code-scanning/alerts/(\\d+)`, (ctx, [o, n, num]) => updateCodeAlertApi(ctx, o, n, pullNumber(num)), ), route("GET", `${REPO}/pulls/(\\d+)/code-scanning`, (ctx, [o, n, num]) => getPullCodeAlertsApi(ctx, o, n, pullNumber(num)), ), route("GET", `${REPO}/dependencies`, (ctx, [o, n]) => getDependenciesApi(ctx, o, n)), selfService("POST", `${REPO}/commits/signatures`, (ctx, [o, n]) => getCommitSignaturesApi(ctx, o, n)), route("GET", `${REPO}/security-advisories`, (ctx, [o, n]) => listAdvisoriesApi(ctx, o, n)), route("POST", `${REPO}/security-advisories`, (ctx, [o, n]) => createAdvisoryApi(ctx, o, n)), route("GET", `${REPO}/security-advisories/${SEG}`, (ctx, [o, n, id]) => getAdvisoryApi(ctx, o, n, id)), route("PATCH", `${REPO}/security-advisories/${SEG}`, (ctx, [o, n, id]) => updateAdvisoryApi(ctx, o, n, id)), route("PUT", `${REPO}/security-advisories/${SEG}/state`, (ctx, [o, n, id]) => setAdvisoryStateApi(ctx, o, n, id)), route("POST", `${REPO}/security-advisories/${SEG}/comments`, (ctx, [o, n, id]) => commentAdvisoryApi(ctx, o, n, id)), route("PUT", `${REPO}/security-advisories/${SEG}/collaborators/${SEG}`, (ctx, [o, n, id, u]) => setAdvisoryCollaboratorApi(ctx, o, n, id, u, true), ), route("DELETE", `${REPO}/security-advisories/${SEG}/collaborators/${SEG}`, (ctx, [o, n, id, u]) => setAdvisoryCollaboratorApi(ctx, o, n, id, u, false), ), route("GET", "/user/credential", (ctx) => getCredential(ctx)), route("GET", `${REPO}/permissions`, (ctx, [o, n]) => getRepoPermissions(ctx, o, n)), route("GET", `${REPO}/issues`, (ctx, [o, n]) => listIssues(ctx, o, n)), route("POST", `${REPO}/issues`, (ctx, [o, n]) => createIssue(ctx, o, n)), route("GET", `${REPO}/issues/(\\d+)`, (ctx, [o, n, num]) => getIssue(ctx, o, n, issueNumber(num))), route("PATCH", `${REPO}/issues/(\\d+)`, (ctx, [o, n, num]) => updateIssue(ctx, o, n, issueNumber(num))), route("GET", `${REPO}/issues/(\\d+)/comments`, (ctx, [o, n, num]) => listComments(ctx, o, n, issueNumber(num))), route("POST", `${REPO}/issues/(\\d+)/comments`, (ctx, [o, n, num]) => createComment(ctx, o, n, issueNumber(num))), route("GET", `${REPO}/branches`, (ctx, [o, n]) => listBranchesApi(ctx, o, n)), route("POST", `${REPO}/branches`, (ctx, [o, n]) => createBranchApi(ctx, o, n)), route("DELETE", `${REPO}/branches/(.+)`, (ctx, [o, n, b]) => deleteBranchApi(ctx, o, n, b)), route("GET", `${REPO}/tags`, (ctx, [o, n]) => listTagsApi(ctx, o, n)), route("POST", `${REPO}/transfer`, (ctx, [o, n]) => transferRepoApi(ctx, o, n)), route("POST", "/orgs", (ctx) => createOrgApi(ctx)), route("GET", `/orgs/${SEG}`, (ctx, [org]) => getOrgApi(ctx, org)), route("GET", `/orgs/${SEG}/members`, (ctx, [org]) => listOrgMembers(ctx, org)), route("PUT", `/orgs/${SEG}/members/${SEG}`, (ctx, [org, u]) => putOrgMember(ctx, org, u)), route("DELETE", `/orgs/${SEG}/members/${SEG}`, (ctx, [org, u]) => deleteOrgMember(ctx, org, u)), route("GET", `/orgs/${SEG}/invitations`, (ctx, [org]) => listOrgInvitationsApi(ctx, org)), route("GET", "/user/organization_invitations", (ctx) => listMyOrgInvitationsApi(ctx)), route("POST", `/user/organization_invitations/${SEG}/accept`, (ctx, [org]) => acceptOrgInvitationApi(ctx, org)), selfService("POST", `/user/organization_invitations/${SEG}/decline`, (ctx, [org]) => declineOrgInvitationApi(ctx, org), ), route("DELETE", `${REPO}/collaborators/${SEG}`, (ctx, [o, n, u]) => deleteCollaborator(ctx, o, n, u)), route("GET", `${REPO}/site`, (ctx, [o, n]) => getSiteApi(ctx, o, n)), route("PUT", `${REPO}/site`, (ctx, [o, n]) => putSiteApi(ctx, o, n)), route("DELETE", `${REPO}/site`, (ctx, [o, n]) => deleteSiteApi(ctx, o, n)), route("PUT", `${REPO}/site/access`, (ctx, [o, n]) => putSiteAccessApi(ctx, o, n)), route("PUT", `${REPO}/site/variables/${SEG}`, (ctx, [o, n, v]) => putSiteVariableApi(ctx, o, n, v)), route("DELETE", `${REPO}/site/variables/${SEG}`, (ctx, [o, n, v]) => deleteSiteVariableApi(ctx, o, n, v)), route("POST", `${REPO}/site/deployments`, (ctx, [o, n]) => deploySiteApi(ctx, o, n)), route("POST", `${REPO}/site/rollback`, (ctx, [o, n]) => rollbackSiteApi(ctx, o, n)), route("PUT", `${REPO}/site/domain`, (ctx, [o, n]) => putSiteDomainApi(ctx, o, n)), route("POST", `${REPO}/site/domain/check`, (ctx, [o, n]) => checkSiteDomainApi(ctx, o, n)), route("GET", "/user/lfs-quota", (ctx) => getLfsQuota(ctx)), route("GET", "/user/notification-settings", (ctx) => getNotificationSettingsApi(ctx)), selfService("PUT", "/user/notification-settings", (ctx) => putNotificationSettingsApi(ctx)), route("GET", "/notifications", (ctx) => listNotificationsApi(ctx)), selfService("PUT", "/notifications", (ctx) => markNotificationsApi(ctx)), route("GET", `/orgs/${SEG}/integrations`, (ctx, [o]) => listHooks(ctx, o, null)), route("POST", `/orgs/${SEG}/integrations`, (ctx, [o]) => createHook(ctx, o, null)), route("DELETE", `/orgs/${SEG}/integrations/${SEG}`, (ctx, [o, id]) => deleteHook(ctx, o, null, id)), route("POST", `/orgs/${SEG}/integrations/${SEG}/pings`, (ctx, [o, id]) => pingHook(ctx, o, null, id)), route("POST", `/orgs/${SEG}/integrations/${SEG}/deliveries/${SEG}/attempts`, (ctx, [o, id, d]) => redeliverHook(ctx, o, null, id, d), ), route("GET", `${REPO}/integrations`, (ctx, [o, n]) => listHooks(ctx, o, n)), route("POST", `${REPO}/integrations`, (ctx, [o, n]) => createHook(ctx, o, n)), route("DELETE", `${REPO}/integrations/${SEG}`, (ctx, [o, n, id]) => deleteHook(ctx, o, n, id)), route("POST", `${REPO}/integrations/${SEG}/pings`, (ctx, [o, n, id]) => pingHook(ctx, o, n, id)), route("POST", `${REPO}/integrations/${SEG}/deliveries/${SEG}/attempts`, (ctx, [o, n, id, d]) => redeliverHook(ctx, o, n, id, d), ), route("GET", `${REPO}/hooks`, (ctx, [o, n]) => listWebhooksApi(ctx, o, n)), route("POST", `${REPO}/hooks`, (ctx, [o, n]) => createWebhookApi(ctx, o, n)), route("DELETE", `${REPO}/hooks/${SEG}`, (ctx, [o, n, id]) => deleteWebhookApi(ctx, o, n, id)), route("GET", `${REPO}/hooks/${SEG}/deliveries`, (ctx, [o, n, id]) => listWebhookDeliveriesApi(ctx, o, n, id)), route("POST", `${REPO}/hooks/${SEG}/deliveries/${SEG}/attempts`, (ctx, [o, n, id, d]) => redeliverWebhookApi(ctx, o, n, id, d), ), route("GET", `${REPO}/actions/runs/(\\d+)`, (ctx, [o, n, num]) => getRunApi(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/actions/runs/(\\d+)/rerun`, (ctx, [o, n, num]) => rerunRunApi(ctx, o, n, pullNumber(num))), route("POST", `${REPO}/actions/workflows/dispatches`, (ctx, [o, n]) => dispatchWorkflowApi(ctx, o, n)), route("GET", `${REPO}/actions/secrets`, (ctx, [o, n]) => listSecretsApi(ctx, o, n)), route("PUT", `${REPO}/actions/secrets/${SEG}`, (ctx, [o, n, k]) => putSecretApi(ctx, o, n, k)), route("DELETE", `${REPO}/actions/secrets/${SEG}`, (ctx, [o, n, k]) => deleteSecretApi(ctx, o, n, k)), route("GET", `${REPO}/actions/variables`, (ctx, [o, n]) => listVariablesApi(ctx, o, n)), route("PUT", `${REPO}/actions/variables/${SEG}`, (ctx, [o, n, k]) => putVariableApi(ctx, o, n, k)), route("DELETE", `${REPO}/actions/variables/${SEG}`, (ctx, [o, n, k]) => deleteVariableApi(ctx, o, n, k)), route("GET", `${REPO}/environments/${SEG}/secrets`, (ctx, [o, n, e]) => listSecretsApi(ctx, o, n, e)), route("PUT", `${REPO}/environments/${SEG}/secrets/${SEG}`, (ctx, [o, n, e, k]) => putSecretApi(ctx, o, n, k, e)), route("DELETE", `${REPO}/environments/${SEG}/secrets/${SEG}`, (ctx, [o, n, e, k]) => deleteSecretApi(ctx, o, n, k, e), ), route("GET", `${REPO}/environments/${SEG}/variables`, (ctx, [o, n, e]) => listVariablesApi(ctx, o, n, e)), route("PUT", `${REPO}/environments/${SEG}/variables/${SEG}`, (ctx, [o, n, e, k]) => putVariableApi(ctx, o, n, k, e)), route("DELETE", `${REPO}/environments/${SEG}/variables/${SEG}`, (ctx, [o, n, e, k]) => deleteVariableApi(ctx, o, n, k, e), ), route("GET", `${REPO}/deployments`, (ctx, [o, n]) => listDeploymentsApi(ctx, o, n)), route("POST", `${REPO}/deployments/${SEG}/review`, (ctx, [o, n, id]) => reviewDeploymentApi(ctx, o, n, id)), route("GET", `${REPO}/actions/caches`, (ctx, [o, n]) => listCachesApi(ctx, o, n)), route("DELETE", `${REPO}/actions/caches/${SEG}`, (ctx, [o, n, id]) => deleteCacheApi(ctx, o, n, id)), route("GET", `${REPO}/commits/${SEG}/statuses`, (ctx, [o, n, sha]) => listStatusesApi(ctx, o, n, sha)), ]; function decodeSegments(match: RegExpExecArray): string[] { try { return match.slice(1).map((part) => decodeURIComponent(part)); } catch { throw new HttpError(400, "Malformed path"); } } /** Dispatches `/api/v1/*`; errors are thrown as HttpError and rendered as `{error}` JSON by the Worker entry. */ export async function handleApiV1(request: Request, url: URL): Promise { const path = url.pathname.slice(API_V1_PREFIX.length - 1).replace(/\/+$/, "") || "/"; if (path === SCRATCH_API_PATH) return handleScratchApi(request); const matching = ROUTES.filter((candidate) => candidate.pattern.test(path)); if (matching.length === 0) return Response.json({ error: "Not found" }, { status: 404 }); const selected = matching.find((candidate) => candidate.method === request.method); if (!selected) { return Response.json( { error: "Method not allowed" }, { status: 405, headers: { allow: matching.map((candidate) => candidate.method).join(", ") } }, ); } const ctx = await apiContext(request, url); if (request.method === "GET") await limitBrowse(ctx.viewer, request); else if (!selected.selfService) assertVerifiedForWrites(ctx.viewer); const match = selected.pattern.exec(path); return selected.handler(ctx, match ? decodeSegments(match) : []); }