@preventive/triage 1.0.0-alpha.49 → 1.0.0-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/common/managed/finding-detail.d.ts +2 -0
- package/common/managed/finding-detail.js +32 -0
- package/common/managed/github-agents.ts +6 -3
- package/common/managed/routes.js +14 -2
- package/out/activity.js +4 -4
- package/out/{chunk-ULIF7A7N.js → chunk-2WDZPXVL.js} +1 -1
- package/out/chunk-4XMP7YVH.js +36 -0
- package/out/chunk-6S2U2UYG.js +232 -0
- package/out/{chunk-IO7JTZ6O.js → chunk-75YLHBCM.js} +1 -1
- package/out/chunk-FC65TPUB.js +1 -0
- package/out/{chunk-O6M574JI.js → chunk-FKBXNUW4.js} +1 -1
- package/out/{chunk-PBSOL2SL.js → chunk-HTBYHSPG.js} +1 -1
- package/out/{chunk-SZD5CW5R.js → chunk-UK7RGJT6.js} +2 -2
- package/out/client-managed-import.js +1 -1
- package/out/client-managed.js +2 -2
- package/out/compare.js +14 -14
- package/out/dialogs.js +34 -30
- package/out/graph.js +1 -1
- package/out/index.html +6 -6
- package/out/scan.js +2 -2
- package/out/view.css +1 -1
- package/out/view.js +15 -15
- package/package.json +4 -1
- package/server-managed/agent-merges.ts +31 -136
- package/server-managed/app-agents.ts +4 -0
- package/server-managed/commit-coauthors.ts +46 -24
- package/server-managed/http.ts +115 -37
- package/server-managed/npm-kept.ts +8 -0
- package/server-managed/public-workspace.ts +2 -2
- package/server-managed/repository-agents.ts +189 -57
- package/server-managed/repository-history.ts +51 -13
- package/server-managed/repository-pulls.ts +156 -0
- package/server-managed/repository-pulse.ts +3 -2
- package/server-managed/repository-squashes.ts +29 -6
- package/server-managed/team-reports.ts +33 -18
- package/out/chunk-BC2UCLFJ.js +0 -1
- package/out/chunk-C2AVODGZ.js +0 -36
- package/out/chunk-JEAUAJVF.js +0 -232
package/server-managed/http.ts
CHANGED
|
@@ -135,8 +135,9 @@ import { mergeLinkGroups } from './link-reports.ts'
|
|
|
135
135
|
import { backfillFindingIds, reportEntries } from '@preventive/report'
|
|
136
136
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
137
137
|
import { pipeline } from 'node:stream/promises'
|
|
138
|
-
import { type KeptStore, REPOSITORY_READ_KEPT_MS, codeHeadStore, codeTreeStore, historyChangeStore, historyPageStore, historySquashStore, historyTreeStore,
|
|
139
|
-
|
|
138
|
+
import { type KeptStore, REPOSITORY_READ_KEPT_MS, codeHeadStore, codeTreeStore, commitPullStore, historyChangeStore, historyPageStore, historySquashStore, historyTreeStore,
|
|
139
|
+
lockfileReadStore,
|
|
140
|
+
npmDeltaStore, npmPublishTimeStore, npmVersionStore, ownKeptPrunes, pullDependencyStore, pullListStore, repositoryReadStore, reviewBaselineStore, riskLineStore, scanReadStore } from './npm-kept.ts'
|
|
140
141
|
import { plainObject } from './npm-manifest.ts'
|
|
141
142
|
import { type NpmDelta, keptNpmDeltas, npmDelta } from './npm-delta.ts'
|
|
142
143
|
import { ADVISORIES_TIMEOUT_MS, fetchBundleAdvisories } from './bundle-advisories.ts'
|
|
@@ -190,7 +191,7 @@ import { isExactVersion } from '@preventive/upstream/semver.js'
|
|
|
190
191
|
import { VIEW_AS_PATH, VIEW_ONLY_ERROR, clearViewCookie, endViewSession, runViewing, startViewSession, viewToken } from './view-as.ts'
|
|
191
192
|
import type { ActivityContext, ActivityInput } from './activity.ts'
|
|
192
193
|
import { acceptsReportMetadata } from './report-response.ts'
|
|
193
|
-
import { TeamReportsError, loadTeamAnnotations, loadTeamReportsResponse,
|
|
194
|
+
import { TeamReportsError, type TeamResponsePart, loadTeamAnnotations, loadTeamReportsResponse, recheckTeam, teamFindingIds, teamResponsePart, teamSnapshot, teamSourcePaths, teamWorkspaceFindingIds } from './team-reports.ts'
|
|
194
195
|
import { FINDING_CATALOG_PAGE_BYTES, FINDING_CATALOG_PAGE_COUNT, MAX_REPORT_QUERY_BYTES, MAX_REPORT_QUERY_COUNT } from './report-query.ts'
|
|
195
196
|
import { lookupFixes, storedFixUrls } from './github-pulls.ts'
|
|
196
197
|
import { lookupIssueLinks } from './github-issue-links.ts'
|
|
@@ -214,11 +215,11 @@ import { NpmPackageError, type NpmReader, type NpmVersionDocument, type NpmVersi
|
|
|
214
215
|
import { loadNpmFiles, loadNpmPackageBody, loadNpmTarball } from './npm-loads.ts'
|
|
215
216
|
import { type RepositoryCodeKept, loadRepositoryCodeBody, repositoryCodeFits, repositoryCodeKept } from './repository-code.ts'
|
|
216
217
|
import { type ChangesStore, type CommitChanges, type CommitTree, TREES_READ, changesAsked, changesWithin, directoryFiles, repositoryChanges } from './repository-changes.ts'
|
|
217
|
-
import { COAUTHORS_READ } from './commit-coauthors.ts'
|
|
218
|
+
import { COAUTHORS_READ, coauthorsOf } from './commit-coauthors.ts'
|
|
218
219
|
import { type AllHeads, type HistoryCommit, type HistoryPages, MAX_HISTORY_PAGE, historyFrom, historyStarts, openPullHeads,
|
|
219
220
|
repositoryAllHeads, repositoryAllHistory, repositoryHistory, repositoryRefTips } from './repository-history.ts'
|
|
220
|
-
import { type SquashedPull, type Squashes, squashedPulls } from './repository-squashes.ts'
|
|
221
|
-
import { AGENTS_READ_VERSION, COMMITS_CURSOR, repositoryAgents } from './repository-agents.ts'
|
|
221
|
+
import { type KnownMerges, type SquashedPull, type Squashes, squashedPulls } from './repository-squashes.ts'
|
|
222
|
+
import { AGENTS_READ_VERSION, COMMITS_CURSOR, type CommitPulls, repositoryAgents } from './repository-agents.ts'
|
|
222
223
|
import { ISSUES_CURSOR, ISSUES_READ_VERSION, repositoryIssues, repositoryItem } from './repository-issues.ts'
|
|
223
224
|
import { type GithubAsked, type ListedLockfile, type LockfileRead, type LockfileStore, MAX_LOCKED, PULL_DEPENDENCIES_READ, type PullCommits, type PullDependencies,
|
|
224
225
|
type PullDependencyStore, eachOf, lockfileFormat, pullDependenciesAt } from './pull-dependencies.ts'
|
|
@@ -232,6 +233,8 @@ import { commentReviews } from './pull-comment-reviews.ts'
|
|
|
232
233
|
import { PULSE_WEEKS, PULSE_YEAR_VERSION, type PulseStats, StatsPending, commitStats, defaultHead, periodStats, pulseSince, repositoryActivity, repositoryLines,
|
|
233
234
|
repositoryStats, withLines, yearCommits } from './repository-pulse.ts'
|
|
234
235
|
import { type AgentMerges, agentCommits, agentMerges, directCommits, sameMerges } from './agent-merges.ts'
|
|
236
|
+
import { PULLS_READ_VERSION, type PullList, pullsReadTo, readPulls } from './repository-pulls.ts'
|
|
237
|
+
import { AGENT_MARKS_READ } from './app-agents.ts'
|
|
235
238
|
import { releaseCommits } from './release-commits.ts'
|
|
236
239
|
import { computeBundleDiff } from '../common/bundle-compare-diff.js'
|
|
237
240
|
import type { ReviewChange, ReviewContext, ReviewInput, ReviewedTarballs } from './dependency-reviews.ts'
|
|
@@ -1882,7 +1885,7 @@ async function handleRepositoryHistory(res: ServerResponse, deps: ManagedHttpDep
|
|
|
1882
1885
|
const listed = (body as { commits: HistoryCommit[] }).commits
|
|
1883
1886
|
const none = { pulls: [] as SquashedPull[], pending: false }
|
|
1884
1887
|
const [squashed, openPulls] = token && path === '' ? await Promise.all([
|
|
1885
|
-
squashedPulls(repo, token, listed, historySquashes(deps, repoId), SQUASH_WAIT_MS).then(read => {
|
|
1888
|
+
pulseMerges(deps, repoId).catch(quiet(null)).then(known => squashedPulls(repo, token, listed, historySquashes(deps, repoId), SQUASH_WAIT_MS, globalThis.fetch, known)).then(read => {
|
|
1886
1889
|
if (read.behind) {
|
|
1887
1890
|
const behind = read.behind.catch(quiet(null))
|
|
1888
1891
|
deps.track(behind)
|
|
@@ -1897,10 +1900,11 @@ async function handleRepositoryHistory(res: ServerResponse, deps: ManagedHttpDep
|
|
|
1897
1900
|
const current = await authorize()
|
|
1898
1901
|
if (!current) return
|
|
1899
1902
|
if (!sameRepository(current.repo, access.repo)) { sendJson(res, 409, { error: 'repository-changed' }); return }
|
|
1900
|
-
// Each commit as the tab draws it, its co-authors with it
|
|
1901
|
-
//
|
|
1902
|
-
|
|
1903
|
-
|
|
1903
|
+
// Each commit as the tab draws it, its co-authors with it, told by the
|
|
1904
|
+
// agents the app knows now (coauthorsOf): what Pulse counts of it (its
|
|
1905
|
+
// lines, its author's time) and the addresses kept not sent.
|
|
1906
|
+
const shown = (commit: HistoryCommit) => ({ sha: commit.sha, parents: commit.parents, subject: commit.subject, authorName: commit.authorName,
|
|
1907
|
+
authorLogin: commit.authorLogin, authorId: commit.authorId ?? null, coauthors: coauthorsOf(commit), committedAt: commit.committedAt })
|
|
1904
1908
|
sendJson(res, 200, await withAppUsers(deps.db, { ...body, commits: listed.map(shown),
|
|
1905
1909
|
...pulls.length > 0 ? { pulls: pulls.map(pull => ({ ...pull, commits: pull.commits.map(shown) })) } : {}, ...squashed.pending ? { pullsPending: true } : {},
|
|
1906
1910
|
...openPulls?.length ? { openPulls } : {} }))
|
|
@@ -2074,14 +2078,16 @@ async function keptAnswer<T extends object>(deps: ManagedHttpDeps, key: string,
|
|
|
2074
2078
|
// telling each commit's co-authors (kept since they are read), and with
|
|
2075
2079
|
// `graphql`, only those read with GraphQL, telling its lines and the accounts
|
|
2076
2080
|
// its co-authors' addresses are (a page GitHub's REST API listed tells
|
|
2077
|
-
// neither)
|
|
2078
|
-
|
|
2081
|
+
// neither), and with `marks`, only those telling the marks agents left on
|
|
2082
|
+
// its commits by the rules they are read by now (app-agents.ts
|
|
2083
|
+
// AGENT_MARKS_READ), as the Agents tab reads them; the rest read again.
|
|
2084
|
+
function historyPages(deps: ManagedHttpDeps, repoId: number, graphql = false, marks = false): HistoryPages {
|
|
2079
2085
|
const store = historyPageStore<HistoryCommit[]>(deps.db, deps.config.debug)
|
|
2080
2086
|
const keyOf = (top: string) => `${repoId}\0${COAUTHORS_READ}\0${top}`
|
|
2081
2087
|
const told = (tops: string[], kept: Map<string, { value: HistoryCommit[] }>) => new Map(tops.flatMap(top => {
|
|
2082
2088
|
const page = kept.get(keyOf(top))?.value
|
|
2083
|
-
return Array.isArray(page) && page.every(commit => Array.isArray(commit.
|
|
2084
|
-
? [[top, page] as const] : []
|
|
2089
|
+
return Array.isArray(page) && page.every(commit => Array.isArray(commit.named)) && (!graphql || page.every(commit => commit.additions != null))
|
|
2090
|
+
&& (!marks || page.every(commit => commit.marked === AGENT_MARKS_READ)) ? [[top, page] as const] : []
|
|
2085
2091
|
}))
|
|
2086
2092
|
return {
|
|
2087
2093
|
read: async tops => told(tops, await store.readMany(tops.map(keyOf))),
|
|
@@ -2093,6 +2099,73 @@ function historyPages(deps: ManagedHttpDeps, repoId: number, graphql = false): H
|
|
|
2093
2099
|
}
|
|
2094
2100
|
}
|
|
2095
2101
|
|
|
2102
|
+
// Whether a pull request brought each of repository `repoId`'s commits, as
|
|
2103
|
+
// the Agents tab looks it up of those an agent marked (repository-agents.ts
|
|
2104
|
+
// pulledCommits), kept by the commit.
|
|
2105
|
+
function commitPulls(deps: ManagedHttpDeps, repoId: number): CommitPulls {
|
|
2106
|
+
const store = commitPullStore<{ pulled: boolean }>(deps.db, deps.config.debug)
|
|
2107
|
+
const keyOf = (sha: string) => `${repoId}\0${sha}`
|
|
2108
|
+
return {
|
|
2109
|
+
async read(shas) {
|
|
2110
|
+
const kept = await store.readMany(shas.map(keyOf))
|
|
2111
|
+
return new Map(shas.flatMap(sha => {
|
|
2112
|
+
const pulled = kept.get(keyOf(sha))?.value?.pulled
|
|
2113
|
+
return typeof pulled === 'boolean' ? [[sha, pulled] as const] : []
|
|
2114
|
+
}))
|
|
2115
|
+
},
|
|
2116
|
+
write: (sha, pulled) => store.write(keyOf(sha), { pulled }, Date.now()),
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// How long repository `repoId`'s pull requests kept (keptPulls) are taken as
|
|
2121
|
+
// they are before those updated since are read: less than its readers keep
|
|
2122
|
+
// their answers (keptAnswer), that one read of them serves both.
|
|
2123
|
+
const PULLS_FRESH_MS = 30_000
|
|
2124
|
+
// Each repository's reads of its pull requests, by its id: one at a time,
|
|
2125
|
+
// each after those asked before it.
|
|
2126
|
+
const pullsReads = new Map<number, Promise<unknown>>()
|
|
2127
|
+
|
|
2128
|
+
// Repository `repoId`'s pull requests, as kept (repository-pulls.ts
|
|
2129
|
+
// readPulls), read on where they were read PULLS_FRESH_MS ago or longer, or
|
|
2130
|
+
// not as far back as `need`: as far back as it, for the Pulse; for the
|
|
2131
|
+
// Agents tab (null), as far back as they were read, those updated since read
|
|
2132
|
+
// (a read not finished, on), else their first page. One read of a
|
|
2133
|
+
// repository's at a time, the rest after it, from what it kept.
|
|
2134
|
+
function keptPulls(deps: ManagedHttpDeps, repoId: number, fullName: string, token: string, need: number | null): Promise<PullList> {
|
|
2135
|
+
const store = pullListStore<PullList>(deps.db, deps.config.debug)
|
|
2136
|
+
const key = `${repoId}\0${PULLS_READ_VERSION}\0${AGENTS_READ_VERSION}\0${PULSE_YEAR_VERSION}`
|
|
2137
|
+
const reading = (pullsReads.get(repoId) ?? Promise.resolve()).catch(() => {}).then(async () => {
|
|
2138
|
+
const kept = await store.read(key)
|
|
2139
|
+
const list = kept?.value ?? null
|
|
2140
|
+
if (list && Date.now() - kept!.at < PULLS_FRESH_MS && (need === null || pullsReadTo(list) <= need)) return list
|
|
2141
|
+
const since = pulseSince(52, Date.now())
|
|
2142
|
+
const read = await readPulls(fullName, token, since, list, globalThis.fetch,
|
|
2143
|
+
need ?? (list ? list.next ? since : list.reach ?? since : Date.now()))
|
|
2144
|
+
await store.write(key, read, Date.now())
|
|
2145
|
+
return read
|
|
2146
|
+
})
|
|
2147
|
+
pullsReads.set(repoId, reading)
|
|
2148
|
+
void reading.catch(() => {}).finally(() => { if (pullsReads.get(repoId) === reading) pullsReads.delete(repoId) })
|
|
2149
|
+
return reading
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
// What the Pulse keeps of repository `repoId`'s pull requests merged, as
|
|
2153
|
+
// it tells by them whose its year's commits are (pulseCommits, agentMerges).
|
|
2154
|
+
const pulseMergesKey = (repoId: number) => `${repoId}\0pulse-merges\0${PULSE_YEAR_VERSION}`
|
|
2155
|
+
|
|
2156
|
+
// The pull requests merged as the Pulse last read them through (a read not
|
|
2157
|
+
// finished, none), for the Commits tab to tell those squashed by
|
|
2158
|
+
// (repository-squashes.ts KnownMerges): each's merge commit and its number
|
|
2159
|
+
// (none of one read before numbers were), every one merged from as far
|
|
2160
|
+
// back as the read reaches (the year's start, or as far as a period needed)
|
|
2161
|
+
// till the newest read among them; null where none is kept.
|
|
2162
|
+
async function pulseMerges(deps: ManagedHttpDeps, repoId: number): Promise<KnownMerges | null> {
|
|
2163
|
+
const kept = (await repositoryReadStore(deps.db, deps.config.debug).read(pulseMergesKey(repoId)))?.value as AgentMerges | undefined
|
|
2164
|
+
if (!kept || kept.next || kept.through === null || !Array.isArray(kept.pulls)) return null
|
|
2165
|
+
return { numbers: new Map(kept.pulls.map(([sha, , number]) => [sha, number ?? null])), from: Math.max(kept.reach ?? Number.NEGATIVE_INFINITY, pulseSince(52, Date.now())),
|
|
2166
|
+
to: kept.through }
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2096
2169
|
// The pull requests squashed into repository `repoId`'s commits, kept by
|
|
2097
2170
|
// each commit and the rules their commits' co-authors were read by
|
|
2098
2171
|
// (COAUTHORS_READ), null for one none was (repository-squashes.ts
|
|
@@ -2106,7 +2179,7 @@ function historySquashes(deps: ManagedHttpDeps, repoId: number): Squashes {
|
|
|
2106
2179
|
const kept = await store.readMany(shas.map(keyOf))
|
|
2107
2180
|
return new Map(shas.flatMap(sha => {
|
|
2108
2181
|
const entry = kept.get(keyOf(sha))
|
|
2109
|
-
return entry && (entry.value === null || (Array.isArray(entry.value.commits) && entry.value.commits.every(commit => Array.isArray(commit.
|
|
2182
|
+
return entry && (entry.value === null || (Array.isArray(entry.value.commits) && entry.value.commits.every(commit => Array.isArray(commit.named))))
|
|
2110
2183
|
? [[sha, entry.value] as const] : []
|
|
2111
2184
|
}))
|
|
2112
2185
|
},
|
|
@@ -2213,9 +2286,10 @@ async function handleRepositoryIssues(res: ServerResponse, deps: ManagedHttpDeps
|
|
|
2213
2286
|
|
|
2214
2287
|
// GET /api/admin/repositories/agents: what the code viewer's Agents tab
|
|
2215
2288
|
// reconstructs agents' sessions from (repository-agents.ts): a page
|
|
2216
|
-
// of pull requests
|
|
2217
|
-
//
|
|
2218
|
-
//
|
|
2289
|
+
// of pull requests below the one the cursor `pulls` names, of those kept for
|
|
2290
|
+
// the Pulse too (keptPulls), and of the default branch's commits on from
|
|
2291
|
+
// `commits`, the first page, naming neither, with issues and branches. They
|
|
2292
|
+
// are the whole repository's (readWholeRepository).
|
|
2219
2293
|
async function handleRepositoryAgents(res: ServerResponse, deps: ManagedHttpDeps, cookie: string | undefined, query: URLSearchParams): Promise<void> {
|
|
2220
2294
|
const repoId = Number(query.get('repoId'))
|
|
2221
2295
|
const cursors = { pulls: query.get('pulls'), commits: query.get('commits') }
|
|
@@ -2223,7 +2297,8 @@ async function handleRepositoryAgents(res: ServerResponse, deps: ManagedHttpDeps
|
|
|
2223
2297
|
|| (cursors.commits !== null && !COMMITS_CURSOR.test(cursors.commits))) {
|
|
2224
2298
|
sendJson(res, 400, { error: 'bad-request' }); return
|
|
2225
2299
|
}
|
|
2226
|
-
await readWholeRepository(res, deps, cookie, repoId, (fullName, token) => repositoryAgents(fullName, token, cursors),
|
|
2300
|
+
await readWholeRepository(res, deps, cookie, repoId, (fullName, token) => repositoryAgents(fullName, token, cursors, historyPages(deps, repoId, true, true),
|
|
2301
|
+
commitPulls(deps, repoId), () => keptPulls(deps, repoId, fullName, token, null)),
|
|
2227
2302
|
`agents\0${AGENTS_READ_VERSION}\0${cursors.pulls ?? ''}\0${cursors.commits ?? ''}`)
|
|
2228
2303
|
}
|
|
2229
2304
|
|
|
@@ -2656,9 +2731,10 @@ async function handleRepositoryPulse(res: ServerResponse, deps: ManagedHttpDeps,
|
|
|
2656
2731
|
// its commits as the Commits tab keeps them, with their lines
|
|
2657
2732
|
// (yearCommits): kept by the head it was read from (repositoryReadStore),
|
|
2658
2733
|
// that head kept as an issue's page is; its commits merged from an agent's
|
|
2659
|
-
// pull request told by those merged (agentMerges),
|
|
2660
|
-
//
|
|
2661
|
-
//
|
|
2734
|
+
// pull request told by those merged (agentMerges), of the pull requests
|
|
2735
|
+
// kept for the Agents tab too (keptPulls), what they tell kept as read, the
|
|
2736
|
+
// rest read on by the next answer as the year's commits are, and read again
|
|
2737
|
+
// as an answer kept is, a label changing whose a pull request is without
|
|
2662
2738
|
// moving the head, the year counted again where what they bring changed.
|
|
2663
2739
|
// Read no further back than the period asked (`weeks`) needs, its weeks and
|
|
2664
2740
|
// as many before them, the rest read only as a longer one asks, its count
|
|
@@ -2718,25 +2794,26 @@ async function pulseCommits(deps: ManagedHttpDeps, repoId: number, fullName: str
|
|
|
2718
2794
|
const store = repositoryReadStore(deps.db, deps.config.debug)
|
|
2719
2795
|
const key = `${repoId}\0pulse-year\0${PULSE_YEAR_VERSION}\0${head.value.sha}\0${first}`
|
|
2720
2796
|
const lastKey = `${repoId}\0pulse-year-last`
|
|
2721
|
-
const mergesKey =
|
|
2797
|
+
const mergesKey = pulseMergesKey(repoId)
|
|
2722
2798
|
const last = async () => {
|
|
2723
2799
|
const kept = (await store.read(lastKey))?.value as { first: number; stats: PulseStats } | undefined
|
|
2724
2800
|
return kept && first - kept.first <= PULSE_LAST_MS ? kept.stats : null
|
|
2725
2801
|
}
|
|
2726
2802
|
const sha = head.value.sha
|
|
2727
2803
|
// What tells whose the year's commits are, kept as read: the agents' pull
|
|
2728
|
-
// requests merged (agentMerges),
|
|
2804
|
+
// requests merged (agentMerges), as the pull requests kept tell, read as
|
|
2805
|
+
// far back as `whole` (keptPulls), and the commits that release the
|
|
2729
2806
|
// repository (`releases`, release-commits.ts releaseCommits).
|
|
2730
2807
|
type Attribution = AgentMerges & { releases?: string[] }
|
|
2731
2808
|
const keptAttribution = async () => ((await store.read(mergesKey))?.value as Attribution | undefined) ?? null
|
|
2732
|
-
const attribution = async (
|
|
2733
|
-
const [
|
|
2809
|
+
const attribution = async (whole: number): Promise<Attribution> => {
|
|
2810
|
+
const [pulls, releases] = await Promise.all([keptPulls(deps, repoId, fullName, token, whole),
|
|
2734
2811
|
releaseCommits(fullName, token, first, historyChanges(deps, repoId))])
|
|
2735
|
-
return { ...
|
|
2812
|
+
return { ...agentMerges(pulls, first), releases }
|
|
2736
2813
|
}
|
|
2737
|
-
// Since when every pull request merged is read (
|
|
2738
|
-
//
|
|
2739
|
-
const readTo = (read: Attribution) => read
|
|
2814
|
+
// Since when every pull request merged is read (repository-pulls.ts
|
|
2815
|
+
// pullsReadTo): all of them, -∞; none yet, ∞.
|
|
2816
|
+
const readTo = (read: Attribution) => pullsReadTo(read)
|
|
2740
2817
|
// The year counted from its `commits` as `read` tells whose they are and
|
|
2741
2818
|
// which were pushed directly, the branch followed through those it was
|
|
2742
2819
|
// read on past (`passed`, yearCommits) too, whole from when both the
|
|
@@ -2761,7 +2838,7 @@ async function pulseCommits(deps: ManagedHttpDeps, repoId: number, fullName: str
|
|
|
2761
2838
|
const whole = stats.from ?? first
|
|
2762
2839
|
await keptAnswer(deps, mergesKey, async () => {
|
|
2763
2840
|
const kept = await keptAttribution()
|
|
2764
|
-
const read = await attribution(
|
|
2841
|
+
const read = await attribution(whole)
|
|
2765
2842
|
const same = kept && sameMerges(read, kept) && read.releases!.join() === (kept.releases ?? []).join() && readTo(kept) <= readTo(read)
|
|
2766
2843
|
if (readTo(read) >= whole || same) return read
|
|
2767
2844
|
const year = await yearCommits(fullName, token, sha, first, historyPages(deps, repoId, true), globalThis.fetch, whole)
|
|
@@ -2779,7 +2856,7 @@ async function pulseCommits(deps: ManagedHttpDeps, repoId: number, fullName: str
|
|
|
2779
2856
|
}
|
|
2780
2857
|
if (!held || !('tooMany' in held)) {
|
|
2781
2858
|
const [year, read] = await Promise.all([yearCommits(fullName, token, sha, first, historyPages(deps, repoId, true), globalThis.fetch, need),
|
|
2782
|
-
|
|
2859
|
+
attribution(need)])
|
|
2783
2860
|
await store.write(mergesKey, read, Date.now())
|
|
2784
2861
|
if (year.complete) {
|
|
2785
2862
|
if (readTo(read) >= need) return { stats: await last(), reading: null, pulls: read.next?.read ?? 0, pending: false, kept: false }
|
|
@@ -3714,11 +3791,12 @@ async function handleQueryReports(req: IncomingMessage, res: ServerResponse, dep
|
|
|
3714
3791
|
|
|
3715
3792
|
// The server selects the complete published workspace for classification.
|
|
3716
3793
|
// Privileged hidden-report previews are independently scoped and bounded.
|
|
3717
|
-
// Its reports' file trees are asked for apart, by its graph or Files alone
|
|
3794
|
+
// Its reports' file trees are asked for apart, by its graph or Files alone,
|
|
3795
|
+
// and its board's reports without their findings' detail (`?lite=1`).
|
|
3718
3796
|
async function handleTeamReports(res: ServerResponse, deps: ManagedHttpDeps, session: ManagedSession, teamId: string, reportId: string | null,
|
|
3719
|
-
part:
|
|
3797
|
+
part: TeamResponsePart = 'reports'): Promise<void> {
|
|
3720
3798
|
const snapshot = await teamSnapshot(deps.db, session.id, teamId, reportId)
|
|
3721
|
-
const body = await
|
|
3799
|
+
const body = await loadTeamReportsResponse(deps.db, deps.reportStore, snapshot, part)
|
|
3722
3800
|
await recheckTeam(deps.db, session.id, snapshot)
|
|
3723
3801
|
res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store', 'x-content-type-options': 'nosniff' })
|
|
3724
3802
|
writeResponse(res, body)
|
|
@@ -5092,7 +5170,7 @@ export function createManagedRequestHandler(deps: ManagedHttpDeps): Handler {
|
|
|
5092
5170
|
const teamReports = /^\/api\/teams\/([^/]+)\/(reports|trees)$/u.exec(path)
|
|
5093
5171
|
if (teamReports) {
|
|
5094
5172
|
if (method !== 'GET') { send405(res, 'GET'); return }
|
|
5095
|
-
await handleTeamReports(res, deps, workspaceSession!.session, teamReports[1]!, url.searchParams.get('reportId'), teamReports[2]
|
|
5173
|
+
await handleTeamReports(res, deps, workspaceSession!.session, teamReports[1]!, url.searchParams.get('reportId'), teamResponsePart(teamReports[2]!, url)); return
|
|
5096
5174
|
}
|
|
5097
5175
|
if (path === REPORT_QUERY_PATH) {
|
|
5098
5176
|
if (method !== 'POST') { send405(res, 'POST'); return }
|
|
@@ -161,6 +161,14 @@ export const historyPageStore = <T>(db: UpstreamCacheStore, debug = false): Kept
|
|
|
161
161
|
// The pull requests squashed into a repository's commits, by the commit
|
|
162
162
|
// (repository-squashes.ts squashedPulls): kept as its history's pages are.
|
|
163
163
|
export const historySquashStore = <T>(db: UpstreamCacheStore, debug = false): KeptStore<T> => keptStore<T>(db, 'repository/squashes', HISTORY_PAGE_KEPT_MS, debug)
|
|
164
|
+
// Whether a pull request brought a commit, by its repository and commit, as
|
|
165
|
+
// the Agents tab looks it up of those an agent marked (repository-agents.ts
|
|
166
|
+
// pulledCommits), kept as the history is.
|
|
167
|
+
export const commitPullStore = <T>(db: UpstreamCacheStore, debug = false): KeptStore<T> => keptStore<T>(db, 'repository/commit-pulls', HISTORY_PAGE_KEPT_MS, debug)
|
|
168
|
+
// A repository's pull requests, the most recently updated first, as the
|
|
169
|
+
// Pulse and the Agents tab read them (repository-pulls.ts readPulls), by its
|
|
170
|
+
// id: kept as its history's pages are, those updated since read on.
|
|
171
|
+
export const pullListStore = <T>(db: UpstreamCacheStore, debug = false): KeptStore<T> => keptStore<T>(db, 'repository/pulls', HISTORY_PAGE_KEPT_MS, debug)
|
|
164
172
|
// The files each of a repository's commits changes (repository-changes.ts
|
|
165
173
|
// repositoryChanges), by the commit, which never change: kept as its
|
|
166
174
|
// history's pages are.
|
|
@@ -7,7 +7,7 @@ import type { OpenedBlob } from './blob-store.ts'
|
|
|
7
7
|
import type { WorkspaceShareSnapshot } from './workspace-shares.ts'
|
|
8
8
|
import { sendJson, sendLines, writeResponse } from './http-response.ts'
|
|
9
9
|
import { hashToken } from './crypto.ts'
|
|
10
|
-
import { TeamReportsError, loadTeamAnnotations, loadTeamReportsResponse,
|
|
10
|
+
import { TeamReportsError, loadTeamAnnotations, loadTeamReportsResponse, teamReportVisibility, teamResponsePart } from './team-reports.ts'
|
|
11
11
|
import { triageWireEntry } from './triage-response.ts'
|
|
12
12
|
import { MAX_PACKAGE_INVENTORY_BYTES } from './bundle-cache.ts'
|
|
13
13
|
import { backfillBundleSummaries, bundleSummaries } from './bundle-catalog.ts'
|
|
@@ -84,7 +84,7 @@ export async function handlePublicWorkspace(req: IncomingMessage, res: ServerRes
|
|
|
84
84
|
await send(await loadTeamAnnotations(deps.db, deps.reportStore, snapshot, url.searchParams.get('reportId'))); return
|
|
85
85
|
}
|
|
86
86
|
if (teamRoute[2] === 'shared') { await sendTeam(); return }
|
|
87
|
-
const body = await
|
|
87
|
+
const body = await loadTeamReportsResponse(deps.db, deps.reportStore, snapshot, teamResponsePart(teamRoute[2]!, url))
|
|
88
88
|
await recheck()
|
|
89
89
|
res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'private, no-store', 'referrer-policy': 'no-referrer' })
|
|
90
90
|
writeResponse(res, body); return
|