@preventive/triage 1.0.0-alpha.46 → 1.0.0-alpha.48
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/client/dependency-paths.d.ts +2 -1
- package/client/dependency-paths.js +16 -4
- package/common/managed/agent-tasks.ts +12 -12
- package/common/managed/github-agents.ts +29 -10
- package/out/activity.js +4 -0
- package/out/chunk-6LNQTKAV.js +52 -0
- package/out/chunk-7BKKYVWR.js +7 -0
- package/out/chunk-7VAWINQF.js +6 -0
- package/out/chunk-C2AVODGZ.js +36 -0
- package/out/{chunk-I2MDGSQX.js → chunk-IO7JTZ6O.js} +1 -1
- package/out/chunk-MOACFRJ4.js +3 -0
- package/out/chunk-O6M574JI.js +1 -0
- package/out/chunk-PBZPYIG7.js +1 -0
- package/out/chunk-PENGGKX7.js +233 -0
- package/out/{chunk-OKRLLE5I.js → chunk-PWFUOLZK.js} +1 -1
- package/out/chunk-SZD5CW5R.js +3 -0
- package/out/{chunk-G24A42V6.js → chunk-TQJDYHVE.js} +1 -1
- package/out/{chunk-3PS4KWD2.js → chunk-WCPJDAYA.js} +1 -1
- package/out/{chunk-7XRR4OFU.js → chunk-WYLQWYLA.js} +1 -1
- package/out/chunk-X6PJAK3T.js +1 -0
- package/out/client-managed-import.js +1 -1
- package/out/client-managed.js +26 -26
- package/out/client-sync.js +2 -2
- package/out/compare.js +14 -14
- package/out/dialogs.js +40 -40
- package/out/graph.js +11 -11
- package/out/index.html +12 -12
- package/out/scan.js +2 -2
- package/out/terminal.js +215 -215
- package/out/view.css +1 -1
- package/out/view.js +15 -15
- package/package.json +9 -5
- package/risk/index.d.ts +4 -2
- package/risk/npm/assess.js +15 -4
- package/risk/npm/registry.js +6 -1
- package/risk/npm/score.js +1 -1
- package/server-managed/activity.ts +24 -11
- package/server-managed/agent-merges.ts +10 -8
- package/server-managed/app-agents.ts +8 -21
- package/server-managed/bundle-build-worker.js +7 -3
- package/server-managed/bundle-build.ts +5 -3
- package/server-managed/bundle-cache.ts +9 -3
- package/server-managed/bundle.ts +3 -4
- package/server-managed/cache-storage.ts +3 -2
- package/server-managed/commit-coauthors.ts +11 -10
- package/server-managed/commit-lockfiles.ts +22 -21
- package/server-managed/db-methods.ts +338 -187
- package/server-managed/db-neon.ts +31 -8
- package/server-managed/db.ts +2 -0
- package/server-managed/dependabot-access.ts +1 -1
- package/server-managed/dependency-review-comments.ts +7 -4
- package/server-managed/dependency-reviews.ts +71 -33
- package/server-managed/github-accounts.ts +28 -1
- package/server-managed/github-app.ts +27 -23
- package/server-managed/github-graphql.ts +39 -0
- package/server-managed/github-issue-links.ts +8 -1
- package/server-managed/github-metadata.ts +2 -2
- package/server-managed/http-response.ts +28 -10
- package/server-managed/http.ts +736 -247
- package/server-managed/index.ts +2 -2
- package/server-managed/link-reports.ts +5 -1
- package/server-managed/managed-issues.ts +3 -0
- package/server-managed/npm-delta.ts +16 -0
- package/server-managed/npm-kept.ts +51 -10
- package/server-managed/npm-loads.ts +48 -28
- package/server-managed/npm-manifest.ts +1 -1
- package/server-managed/npm-packages.ts +31 -15
- package/server-managed/npm-risk.ts +64 -17
- package/server-managed/object-storage.ts +4 -3
- package/server-managed/pretty-print.ts +45 -12
- package/server-managed/public-workspace.ts +3 -3
- package/server-managed/pull-dependencies.ts +84 -36
- package/server-managed/pull-dependency-reviews.ts +13 -10
- package/server-managed/rename-diff-worker.js +1 -1
- package/server-managed/report-store.ts +16 -11
- package/server-managed/repository-agents.ts +9 -6
- package/server-managed/repository-browser.ts +17 -5
- package/server-managed/repository-code.ts +180 -17
- package/server-managed/repository-dependency-reviews.ts +7 -0
- package/server-managed/repository-history.ts +85 -152
- package/server-managed/repository-issues.ts +27 -13
- package/server-managed/repository-pulse.ts +9 -3
- package/server-managed/repository-squashes.ts +145 -0
- package/server-managed/request-timing.ts +63 -0
- package/server-managed/reviewed-dependencies.ts +3 -2
- package/server-managed/session.ts +34 -12
- package/server-managed/solidity-entry-points.ts +5 -3
- package/server-managed/sql-postgres.ts +3 -0
- package/server-managed/sql.ts +17 -8
- package/server-managed/storage-db.ts +11 -3
- package/server-managed/storage-encryption.ts +23 -12
- package/server-managed/storage-maintenance.ts +5 -2
- package/server-managed/storage-payload.ts +18 -0
- package/server-managed/storage-stores.ts +3 -2
- package/server-managed/storage.ts +4 -2
- package/server-managed/team-feed.ts +19 -12
- package/server-managed/team-npm-scopes.ts +29 -0
- package/server-managed/team-reports.ts +74 -17
- package/server-managed/uploads.ts +29 -9
- package/out/chunk-4UFAMJAX.js +0 -1
- package/out/chunk-BOIXMUU5.js +0 -3
- package/out/chunk-CC4OM6TZ.js +0 -3
- package/out/chunk-JKGQ3O7O.js +0 -6
- package/out/chunk-LE5ZWLG4.js +0 -235
- package/out/chunk-MSION3G4.js +0 -36
- package/out/chunk-S6GIXWQW.js +0 -52
- package/out/chunk-V3KWNNMY.js +0 -1
- package/out/chunk-WQ74ZBZF.js +0 -7
|
@@ -3,7 +3,7 @@ import { type StorageKey, decryptStorageStream, encryptStorageStream } from '../
|
|
|
3
3
|
import { ENCRYPTED_CACHE_PREFIX, type RawObjectStorage, deleteObjects, isBlobId, openObjectVersion } from './object-storage.ts'
|
|
4
4
|
import { type StorageDb, type StorageEncryptionState, type StorageMigrationRow, type StorageRow, type StorageRowKind, unwrapDataKey } from './storage-db.ts'
|
|
5
5
|
import { STORAGE_UPLOAD_TTL_MS } from './storage-encryption.ts'
|
|
6
|
-
import { StoragePayloadError, inspectStorageObject, storageOwner, storageRowPaths, verifiedStoragePayload, verifyStoragePayload } from './storage-payload.ts'
|
|
6
|
+
import { StoragePayloadError, inspectStorageObject, masterKeyCache, storageOwner, storageRowPaths, verifiedStoragePayload, verifyStoragePayload } from './storage-payload.ts'
|
|
7
7
|
|
|
8
8
|
async function replacementFailure(raw: RawObjectStorage, identity: string, version: string, signal: AbortSignal): Promise<string> {
|
|
9
9
|
const format = (value: string) => value.startsWith('W/') ? 'weak' : value.startsWith('"') && value.endsWith('"') ? 'quoted' : 'unquoted'
|
|
@@ -113,7 +113,10 @@ function cleanupTarget(identity: string) {
|
|
|
113
113
|
const path = temporary ? temp![1]! : identity
|
|
114
114
|
const encryptedCache = path.startsWith(ENCRYPTED_CACHE_PREFIX)
|
|
115
115
|
if (encryptedCache && !temporary) return null
|
|
116
|
-
const
|
|
116
|
+
const logical = encryptedCache ? `cache/${path.slice(ENCRYPTED_CACHE_PREFIX.length)}` : path
|
|
117
|
+
// A cache no row owns is a cache all the same: its plaintext legacy, and
|
|
118
|
+
// its temporary files removed past the staging window.
|
|
119
|
+
const owner = storageOwner(logical) ?? (masterKeyCache(logical) ? { cache: true as const } : null)
|
|
117
120
|
return owner ? { owner, temporary } : null
|
|
118
121
|
}
|
|
119
122
|
|
|
@@ -19,6 +19,24 @@ export function storageRowPaths(type: StorageRowKind, row: Pick<StorageRow, 'id'
|
|
|
19
19
|
// are: public npm versions' pretty-printed files (pretty-print.ts).
|
|
20
20
|
export const PUBLIC_CACHE = 'npm'
|
|
21
21
|
export const PUBLIC_CACHE_PREFIX = `cache/${PUBLIC_CACHE}/`
|
|
22
|
+
// A cache of private content no row owns: the code viewer's directories at
|
|
23
|
+
// repositories' default branches' heads (repository-code.ts
|
|
24
|
+
// repositoryCodeKept), by repository. Plaintext while storage is not
|
|
25
|
+
// encrypted, as other caches are; once it is, kept apart under
|
|
26
|
+
// ENCRYPTED_CACHE_PREFIX as a bundle's caches are, so that what was kept
|
|
27
|
+
// before is never read and legacy cleanup removes it, and encrypted with the
|
|
28
|
+
// master key, as upload parts are, each write's key derived from its path,
|
|
29
|
+
// so that one path's ciphertext fails to authenticate at another. No storage
|
|
30
|
+
// row holds a repository's code for a data key of its own to be wrapped in:
|
|
31
|
+
// its selection is none, and stays as it is deactivated. A key of its own
|
|
32
|
+
// would add a column and its migration only for its deletion to make what is
|
|
33
|
+
// left unreadable at once, as a deleted bundle's caches are; here deletion
|
|
34
|
+
// is physical, by the repository's prefix, at best, as an upload part's is,
|
|
35
|
+
// and what a failed one leaves is a directory as GitHub has it at a commit,
|
|
36
|
+
// read only by a reader GitHub lets read that repository.
|
|
37
|
+
export const REPOSITORY_CODE_CACHE = 'repository-code'
|
|
38
|
+
const REPOSITORY_CODE_CACHE_PREFIX = `cache/${REPOSITORY_CODE_CACHE}/`
|
|
39
|
+
export const masterKeyCache = (identity: string) => identity.startsWith(REPOSITORY_CODE_CACHE_PREFIX)
|
|
22
40
|
export function storageOwner(identity: string): { type: StorageRowKind; id: string; cache: boolean } | null {
|
|
23
41
|
const match = /^(reports|bundles)\/([^/]+)$/u.exec(identity)
|
|
24
42
|
if (match) {
|
|
@@ -8,7 +8,7 @@ import { createBundleStore } from './bundle-store.ts'
|
|
|
8
8
|
import { createReportStore } from './report-store.ts'
|
|
9
9
|
import { CacheMissError, type CacheStorage, validateCacheKey } from './cache-storage.ts'
|
|
10
10
|
import { type ObjectStorage, isBlobId } from './object-storage.ts'
|
|
11
|
-
import { PUBLIC_CACHE } from './storage-payload.ts'
|
|
11
|
+
import { PUBLIC_CACHE, REPOSITORY_CODE_CACHE } from './storage-payload.ts'
|
|
12
12
|
|
|
13
13
|
function id(value: string): string {
|
|
14
14
|
if (!isBlobId(value)) throw new Error('Invalid managed blob id')
|
|
@@ -35,7 +35,7 @@ function cacheStore(objects: ObjectStorage, name: string): CacheStorage {
|
|
|
35
35
|
if (!value) throw new CacheMissError()
|
|
36
36
|
return value
|
|
37
37
|
},
|
|
38
|
-
delete: prefix => objects.deletePrefix(`${path(prefix)}/`),
|
|
38
|
+
delete: (prefix, keep) => objects.deletePrefix(`${path(prefix)}/`, keep === undefined ? undefined : `${path(`${prefix}/${keep}`)}/`),
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -86,5 +86,6 @@ export function createManagedStores(objects: ObjectStorage, avatarSidecar: boole
|
|
|
86
86
|
cacheStorage,
|
|
87
87
|
reportSourcesStorage: cacheStore(objects, 'report-sources'),
|
|
88
88
|
npmCacheStorage: cacheStore(objects, PUBLIC_CACHE),
|
|
89
|
+
repositoryCodeStorage: cacheStore(objects, REPOSITORY_CODE_CACHE),
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -31,11 +31,13 @@ export async function openManagedStorage(config: ManagedConfig) {
|
|
|
31
31
|
const raw = config.neonUrl ? await openVercelObjectStorage(config.blobToken!) : createDiskObjectStorage(dirname(config.dbPath))
|
|
32
32
|
// Enable before exposing stores or token writers. Concurrent first starts
|
|
33
33
|
// serialize activation in the database and validate the winning key.
|
|
34
|
-
|
|
34
|
+
const enabled = key ? await db.getStorageEncryption() : null
|
|
35
|
+
if (key && !enabled) {
|
|
35
36
|
if (config.vercelPreview) throw new Error('Storage encryption must be enabled outside a Vercel preview deployment')
|
|
36
37
|
await db.enableStorageEncryption()
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
// Enabled already: that state, not read again.
|
|
40
|
+
const objects = await createEncryptedObjectStorage(raw, db, key, enabled)
|
|
39
41
|
const storage = createManagedStores(objects, !config.neonUrl)
|
|
40
42
|
// Move legacy team link uploads into encrypted SQL before serving catalogs.
|
|
41
43
|
// The DB transaction commits first; a failed blob cleanup cannot lose links.
|
|
@@ -2,7 +2,7 @@ import type { ServerResponse } from 'node:http'
|
|
|
2
2
|
import { setTimeout as delay } from 'node:timers/promises'
|
|
3
3
|
import type { ManagedHttpDeps } from './http.ts'
|
|
4
4
|
import type { StoredUser, TeamReportAccessSnapshot } from './db.ts'
|
|
5
|
-
import { TeamReportsError, teamSnapshotKey, teamWorkspaceFindingIds } from './team-reports.ts'
|
|
5
|
+
import { TeamReportsError, annotationRevisionDigest, teamAnnotationRevision, teamSnapshotKey, teamWorkspaceFindingIds } from './team-reports.ts'
|
|
6
6
|
import type { WorkspaceShareFeedState } from './workspace-shares.ts'
|
|
7
7
|
|
|
8
8
|
// Short database reads work across instances without pinning a Neon connection.
|
|
@@ -49,7 +49,7 @@ export async function serveTeamFeed(res: ServerResponse, deps: ManagedHttpDeps,
|
|
|
49
49
|
export async function serveUserTeamFeed(res: ServerResponse, deps: ManagedHttpDeps,
|
|
50
50
|
sessionId: string, user: StoredUser, teamId: string | null, options: FeedOptions & { reportId?: string | null } = {}): Promise<void> {
|
|
51
51
|
let ids: string[] = [], visibilityKey: string | undefined
|
|
52
|
-
let
|
|
52
|
+
let focused: TeamReportAccessSnapshot | undefined
|
|
53
53
|
let previousState: { catalog: number; annotations: number } | undefined
|
|
54
54
|
const checkUser = (current: { user: Pick<StoredUser, 'id' | 'role'> } | null) => {
|
|
55
55
|
if (!current) throw new TeamReportsError(401, 'unauthenticated')
|
|
@@ -61,10 +61,10 @@ export async function serveUserTeamFeed(res: ServerResponse, deps: ManagedHttpDe
|
|
|
61
61
|
const state = await deps.db.getFeedState(sessionId, Date.now())
|
|
62
62
|
checkUser(state)
|
|
63
63
|
if (state!.catalog === previousState?.catalog && (!teamId || state!.annotations === previousState?.annotations)) return
|
|
64
|
-
if (teamId &&
|
|
64
|
+
if (teamId && focused !== undefined && state!.catalog === previousState?.catalog) {
|
|
65
65
|
// Annotation changes cannot change visibility. Read only this workspace's
|
|
66
66
|
// IDs, then fence against concurrent access/content changes before sending.
|
|
67
|
-
const triage =
|
|
67
|
+
const triage = await teamAnnotationRevision(deps.db, focused, ids)
|
|
68
68
|
const after = await deps.db.getFeedState(sessionId, Date.now())
|
|
69
69
|
checkUser(after)
|
|
70
70
|
if (after!.catalog !== state!.catalog) return
|
|
@@ -74,7 +74,9 @@ export async function serveUserTeamFeed(res: ServerResponse, deps: ManagedHttpDe
|
|
|
74
74
|
}
|
|
75
75
|
const catalog = await deps.db.getUserTeamFeedSnapshot(sessionId, Date.now())
|
|
76
76
|
checkUser(catalog)
|
|
77
|
-
|
|
77
|
+
// The session read again after the catalog, as the catalog is published
|
|
78
|
+
// next: one statement, as the poll's own check is.
|
|
79
|
+
checkUser(await deps.db.getFeedState(sessionId, Date.now()))
|
|
78
80
|
// Publish catalog state before loading any report blobs. A slow or broken
|
|
79
81
|
// focused report must not hide membership/content changes from the client.
|
|
80
82
|
if (!publish('teams', catalog!.revision)) return
|
|
@@ -101,29 +103,34 @@ export async function serveUserTeamFeed(res: ServerResponse, deps: ManagedHttpDe
|
|
|
101
103
|
return
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
|
-
|
|
106
|
+
// As GET /api/teams/:id/annotations answers it, so that a client holding
|
|
107
|
+
// that answer knows this tells nothing new.
|
|
108
|
+
if (snapshot.teamId) triage = await teamAnnotationRevision(deps.db, snapshot, ids)
|
|
105
109
|
const current = await deps.db.getTeamReportAccessSnapshot(sessionId, Date.now(), teamId, options.reportId)
|
|
106
110
|
// A concurrent access/content change discards only the annotation read.
|
|
107
111
|
checkUser(current)
|
|
108
112
|
if (!current || teamSnapshotKey(current) !== key) return
|
|
109
113
|
publish('triage', triage)
|
|
110
|
-
|
|
114
|
+
focused = snapshot.teamId ? snapshot : undefined
|
|
111
115
|
const after = await deps.db.getFeedState(sessionId, Date.now())
|
|
112
116
|
checkUser(after)
|
|
113
117
|
if (after!.catalog === state!.catalog && after!.annotations === state!.annotations) previousState = state!
|
|
114
|
-
}, options)
|
|
118
|
+
}, { ...options, triageRevision: true })
|
|
115
119
|
}
|
|
116
120
|
|
|
121
|
+
// With `triageRevision`, a triage event tells its revision's digest, as the
|
|
122
|
+
// focused team's annotations answer it; else none, as a public share's.
|
|
117
123
|
async function serveFeed(res: ServerResponse, deps: ManagedHttpDeps, read: (publish: Publish) => Promise<void>,
|
|
118
|
-
{ pollMs = TEAM_FEED_POLL_MS, lifetimeMs = TEAM_FEED_LIFETIME_MS }: FeedOptions): Promise<void> {
|
|
124
|
+
{ pollMs = TEAM_FEED_POLL_MS, lifetimeMs = TEAM_FEED_LIFETIME_MS, triageRevision = false }: FeedOptions & { triageRevision?: boolean }): Promise<void> {
|
|
119
125
|
const controller = new AbortController()
|
|
120
126
|
const close = () => controller.abort()
|
|
121
127
|
res.once('close', close)
|
|
122
128
|
const deadline = setTimeout(close, lifetimeMs)
|
|
123
129
|
const stopped = () => controller.signal.aborted || res.destroyed || deps.isShuttingDown()
|
|
124
130
|
// Notifications carry no annotation bodies, finding IDs, or global cursors.
|
|
125
|
-
// The catalog's opaque version
|
|
126
|
-
//
|
|
131
|
+
// The catalog's opaque version, and a focused team's annotation revision's
|
|
132
|
+
// digest, let clients reuse an identical REST snapshot; every connection
|
|
133
|
+
// still confirms current access, including after reconnects.
|
|
127
134
|
function write(frame: string): boolean {
|
|
128
135
|
if (stopped()) return false
|
|
129
136
|
if (res.write(frame)) return true
|
|
@@ -143,7 +150,7 @@ async function serveFeed(res: ServerResponse, deps: ManagedHttpDeps, read: (publ
|
|
|
143
150
|
if (stopped()) return false
|
|
144
151
|
if (revision === undefined) { previous.delete(event); return true }
|
|
145
152
|
if (revision === previous.get(event)) return true
|
|
146
|
-
const data = event === 'teams' ? JSON.stringify({ revision }) : '{}'
|
|
153
|
+
const data = event === 'teams' ? JSON.stringify({ revision }) : triageRevision ? JSON.stringify({ revision: annotationRevisionDigest(revision) }) : '{}'
|
|
147
154
|
if (!write(`event: ${event}\ndata: ${data}\n\n`)) return false
|
|
148
155
|
previous.set(event, revision)
|
|
149
156
|
heartbeat = Date.now()
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// scope's private packages in the npm viewer (npm-packages.ts), as admins and
|
|
3
3
|
// managers may read any. Hidden teams grant nothing, as for their content.
|
|
4
4
|
import { MAX_TEAM_NPM_SCOPES, normalizeNpmScope } from '../common/managed/npm-packages.js'
|
|
5
|
+
import type { Role } from '../common/managed/roles.ts'
|
|
6
|
+
import { gathered, reviewsIn } from './dependency-reviews.ts'
|
|
5
7
|
import { ManagedMutationError } from './management.ts'
|
|
6
8
|
import type { ManagedSql } from './sql.ts'
|
|
7
9
|
|
|
@@ -23,7 +25,15 @@ export interface TeamNpmScopeStore {
|
|
|
23
25
|
// Whether any visible team lists the scope: whoever reads its packages,
|
|
24
26
|
// they are the instance's own, whose new releases aren't flagged (http.ts).
|
|
25
27
|
hasTeamNpmScope(scope: string): Promise<boolean>
|
|
28
|
+
// Who session `sessionId` names now, as readSession reads it, their own
|
|
29
|
+
// or, where `viewerSessionId` names an admin's own one, a view of it: their
|
|
30
|
+
// role, the scopes their teams list (listUserNpmScopes), and whether they
|
|
31
|
+
// read the dependency reviews published to team `teamId`
|
|
32
|
+
// (dependency-reviews.ts reviewsIn); in one statement, as a list of a
|
|
33
|
+
// team's reviews asks again once npm is read. Null where it names none.
|
|
34
|
+
getNpmReviewReader(sessionId: string, viewerSessionId: string | null, now: number, teamId: string): Promise<NpmReviewReader | null>
|
|
26
35
|
}
|
|
36
|
+
export interface NpmReviewReader { id: string; role: Role; scopes: string[]; readsTeam: boolean }
|
|
27
37
|
|
|
28
38
|
// Each distinct scope, normalized; a list with one npm could not publish
|
|
29
39
|
// under, or too many, is refused whole.
|
|
@@ -68,6 +78,7 @@ export function teamNpmScopeMethods(db: ManagedSql): TeamNpmScopeStore {
|
|
|
68
78
|
WHERE tu.user_id = ? ORDER BY ns.scope`).all(userId) as { scope: string }[]
|
|
69
79
|
return rows.map(row => row.scope)
|
|
70
80
|
},
|
|
81
|
+
getNpmReviewReader: (sessionId, viewerSessionId, now, teamId) => npmReviewReader(db, sessionId, viewerSessionId, now, teamId),
|
|
71
82
|
async hasTeamNpmScope(scope) {
|
|
72
83
|
return !!await db.prepare(`SELECT 1 AS listed FROM managed_team_npm_scope ns
|
|
73
84
|
JOIN managed_team t ON t.id = ns.team_id AND t.hidden = 0
|
|
@@ -75,3 +86,21 @@ export function teamNpmScopeMethods(db: ManagedSql): TeamNpmScopeStore {
|
|
|
75
86
|
},
|
|
76
87
|
}
|
|
77
88
|
}
|
|
89
|
+
|
|
90
|
+
// getNpmReviewReader's statement, its session held to what readSession's
|
|
91
|
+
// own are (db-methods.ts selectOwnSessionStmt, selectViewSessionStmt).
|
|
92
|
+
async function npmReviewReader(db: ManagedSql, sessionId: string, viewerSessionId: string | null, now: number, teamId: string): Promise<NpmReviewReader | null> {
|
|
93
|
+
const row = await db.prepare(`WITH reader AS (
|
|
94
|
+
SELECT u.id AS id, u.role AS role FROM managed_session s JOIN managed_user u ON u.id = s.user_id
|
|
95
|
+
WHERE s.id = ? AND s.expires_at > ? AND ((? IS NULL AND s.viewer_session IS NULL) OR (s.viewer_session = ? AND EXISTS (
|
|
96
|
+
SELECT 1 FROM managed_session p JOIN managed_user a ON a.id = p.user_id
|
|
97
|
+
WHERE p.id = s.viewer_session AND p.expires_at > ? AND p.viewer_session IS NULL AND a.role = 'admin')))
|
|
98
|
+
)
|
|
99
|
+
SELECT v.id AS id, v.role AS role,
|
|
100
|
+
(SELECT json_group_array(x.scope ORDER BY x.scope) FROM (SELECT DISTINCT ns.scope AS scope FROM managed_team_user tu
|
|
101
|
+
JOIN managed_team t ON t.id = tu.team_id AND t.hidden = 0 JOIN managed_team_npm_scope ns ON ns.team_id = tu.team_id
|
|
102
|
+
WHERE tu.user_id = v.id) x) AS scopes,
|
|
103
|
+
CASE WHEN EXISTS (SELECT 1 FROM managed_team t JOIN managed_user u ON u.id = v.id WHERE t.id = ? AND ${reviewsIn(false)}) THEN 1 ELSE 0 END AS readsTeam
|
|
104
|
+
FROM reader v`).get(sessionId, now, viewerSessionId, viewerSessionId, now, teamId) as { id: string; role: Role; scopes: unknown; readsTeam: unknown } | undefined
|
|
105
|
+
return row ? { id: row.id, role: row.role, scopes: gathered<string>(row.scopes), readsTeam: Number(row.readsTeam) === 1 } : null
|
|
106
|
+
}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Bounded snapshots retain filtered content and visibility for repeated reads.
|
|
3
3
|
import { mergeLinkGroups } from './link-reports.ts'
|
|
4
4
|
import { Buffer } from 'node:buffer'
|
|
5
|
+
import { createHash } from 'node:crypto'
|
|
5
6
|
import { backfillFindingIds, reportEntries, stampSecurityGroups } from '@preventive/report'
|
|
6
7
|
import { managedFindingSourcePaths, readManagedReport } from '../common/managed/report-content.ts'
|
|
7
8
|
import { filterReportData, projectFinding } from '../common/managed/report-filter.ts'
|
|
8
9
|
import { revalidationLinks, securityLinks } from '../common/security-links.js'
|
|
10
|
+
import { treeDependencyDirectory } from '../client/dependency-paths.js'
|
|
9
11
|
import type { BlobStore } from './blob-store.ts'
|
|
10
12
|
import type { ManagedDb, TeamReportAccessSnapshot } from './db.ts'
|
|
11
13
|
import { triageWireEntry } from './triage-response.ts'
|
|
@@ -20,18 +22,26 @@ const VISIBILITY_CACHE_MAX_ITEMS = 250_000
|
|
|
20
22
|
const VISIBILITY_CACHE_MAX_SNAPSHOTS = 32
|
|
21
23
|
const caches = new WeakMap<ManagedDb, Map<string, Visibility>>()
|
|
22
24
|
const REPORT_CACHE_BYTES = 16 * 1024 * 1024
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
// A workspace's encoded reports, and its reports' file trees apart.
|
|
26
|
+
type EncodedWorkspace = { reports: Buffer; trees: Buffer }
|
|
27
|
+
const reportCaches = new WeakMap<ManagedDb, Map<string, EncodedWorkspace>>()
|
|
28
|
+
const encodedBytes = ({ reports, trees }: EncodedWorkspace) => reports.length + trees.length
|
|
29
|
+
// A workspace's report reads at once. Its reports' sizes are bounded together
|
|
30
|
+
// before any is read, and each is held parsed till the build ends, so more at
|
|
31
|
+
// once costs only the raw bytes in flight: a cold build of a team's reports
|
|
32
|
+
// is so many storage round trips one after another.
|
|
33
|
+
const WORKSPACE_READS_AT_ONCE = 16
|
|
34
|
+
function retainReports(db: ManagedDb, key: string, encoded: EncodedWorkspace) {
|
|
35
|
+
if (encodedBytes(encoded) > REPORT_CACHE_BYTES) return
|
|
26
36
|
let cache = reportCaches.get(db)
|
|
27
37
|
if (!cache) { cache = new Map(); reportCaches.set(db, cache) }
|
|
28
38
|
cache.delete(key)
|
|
29
|
-
let total =
|
|
39
|
+
let total = encodedBytes(encoded) + [...cache.values()].reduce((sum, value) => sum + encodedBytes(value), 0)
|
|
30
40
|
for (const [oldest, value] of cache) {
|
|
31
41
|
if (total <= REPORT_CACHE_BYTES && cache.size < VISIBILITY_CACHE_MAX_SNAPSHOTS) break
|
|
32
|
-
cache.delete(oldest); total -= value
|
|
42
|
+
cache.delete(oldest); total -= encodedBytes(value)
|
|
33
43
|
}
|
|
34
|
-
cache.set(key,
|
|
44
|
+
cache.set(key, encoded)
|
|
35
45
|
}
|
|
36
46
|
function visibilityWeight(visible: Visibility): number {
|
|
37
47
|
return [...visible.values()].reduce((sum, v) => sum + v.ids.size + v.sourcePaths.size, 0)
|
|
@@ -87,13 +97,32 @@ function linksOf(data: unknown): string[][] | null {
|
|
|
87
97
|
if (!record.links.every(ids => Array.isArray(ids) && ids.every(id => typeof id === 'string'))) throw new TeamReportsError(422, 'unreadable-links')
|
|
88
98
|
return record.links
|
|
89
99
|
}
|
|
90
|
-
|
|
100
|
+
// A report's file tree goes apart, asked for by its graph or Files alone
|
|
101
|
+
// (loadTeamTreesResponse). The report tells in its place how many files it
|
|
102
|
+
// holds (`treeFiles`) and the dependency directory it names
|
|
103
|
+
// (`treeDependencies`), which classifies findings as the tree itself would
|
|
104
|
+
// (client/dependency-paths.js). Each file's is sent without the hash of its
|
|
105
|
+
// tree (`treeHash`), which no team view reads.
|
|
106
|
+
function withoutTree(report: TeamReport): string | null {
|
|
107
|
+
const tree = (report.data as { tree?: unknown } | null)?.tree
|
|
108
|
+
if (!tree || typeof tree !== 'object' || Array.isArray(tree)) return null
|
|
109
|
+
const { tree: _tree, ...data } = report.data as Finding
|
|
110
|
+
const named = treeDependencyDirectory(tree)
|
|
111
|
+
report.data = { ...data, treeFiles: Object.keys(tree).length, ...(named ? { treeDependencies: named } : {}) }
|
|
112
|
+
const files = Object.entries(tree).map(([file, entry]) => {
|
|
113
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return [file, entry]
|
|
114
|
+
const { treeHash: _treeHash, ...kept } = entry as Finding
|
|
115
|
+
return [file, kept]
|
|
116
|
+
})
|
|
117
|
+
return `${JSON.stringify(report.id)}:${JSON.stringify(Object.fromEntries(files))}`
|
|
118
|
+
}
|
|
119
|
+
async function buildTeamWorkspace(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot): Promise<EncodedWorkspace & { visible: Visibility }> {
|
|
91
120
|
if (snapshot.reports.length > MAX_REPORT_QUERY_COUNT || snapshot.reports.reduce((n, r) => n + r.byteSize, 0) > MAX_REPORT_QUERY_BYTES) throw new TeamReportsError(413, 'batch-too-large')
|
|
92
121
|
const reports: TeamReport[] = []
|
|
93
122
|
let inputBytes = 0, next = 0
|
|
94
123
|
// Bound both remote reads and raw buffers, retaining stable report order for
|
|
95
124
|
// the cross-report classification that follows. Finish workers on failure.
|
|
96
|
-
const results = await Promise.allSettled(Array.from({ length: Math.min(
|
|
125
|
+
const results = await Promise.allSettled(Array.from({ length: Math.min(WORKSPACE_READS_AT_ONCE, snapshot.reports.length) }, async () => {
|
|
97
126
|
while (next < snapshot.reports.length) {
|
|
98
127
|
const index = next++
|
|
99
128
|
const access = snapshot.reports[index]!
|
|
@@ -138,7 +167,9 @@ async function buildTeamWorkspace(db: ManagedDb, store: BlobStore, snapshot: Tea
|
|
|
138
167
|
// document even though its referenced findings are outside this response.
|
|
139
168
|
if (links && !snapshot.hiddenReportId) report.data = { source: 'links', findings: [], links: links.map(ids => [...new Set(ids.filter(id => allIds.has(id)))]).filter(ids => ids.length >= 2) }
|
|
140
169
|
}
|
|
141
|
-
|
|
170
|
+
const trees = Buffer.from(`{"trees":{${reports.map(withoutTree).filter(tree => tree !== null).join(',')}}}`)
|
|
171
|
+
// Bounded together, as when the trees were sent with their reports.
|
|
172
|
+
let outputBytes = 14 + trees.length
|
|
142
173
|
const parts = [Buffer.from('{"reports":[')]
|
|
143
174
|
for (const [index, report] of reports.entries()) {
|
|
144
175
|
const bytes = Buffer.from(JSON.stringify(report))
|
|
@@ -151,10 +182,10 @@ async function buildTeamWorkspace(db: ManagedDb, store: BlobStore, snapshot: Tea
|
|
|
151
182
|
const suffix = Buffer.from(links.length > 0 ? `],"links":${JSON.stringify(links)}}` : ']}')
|
|
152
183
|
if (outputBytes + suffix.length > MAX_REPORT_QUERY_BYTES) throw new TeamReportsError(413, 'batch-too-large')
|
|
153
184
|
parts.push(suffix)
|
|
154
|
-
const
|
|
185
|
+
const encoded = { reports: Buffer.concat(parts), trees }
|
|
155
186
|
retainVisibility(db, workspaceKey(snapshot), visible)
|
|
156
|
-
retainReports(db, workspaceKey(snapshot),
|
|
157
|
-
return {
|
|
187
|
+
retainReports(db, workspaceKey(snapshot), encoded)
|
|
188
|
+
return { ...encoded, visible }
|
|
158
189
|
}
|
|
159
190
|
const pendingWorkspaces = new WeakMap<ManagedDb, Map<string, ReturnType<typeof buildTeamWorkspace>>>()
|
|
160
191
|
async function loadTeamWorkspace(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot) {
|
|
@@ -171,7 +202,13 @@ async function loadTeamWorkspace(db: ManagedDb, store: BlobStore, snapshot: Team
|
|
|
171
202
|
// Warm HTTP reads need neither a parsed copy nor another serialization pass.
|
|
172
203
|
export async function loadTeamReportsResponse(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot): Promise<Buffer> {
|
|
173
204
|
const cached = reportCaches.get(db)?.get(workspaceKey(snapshot))
|
|
174
|
-
return cached ?? (await loadTeamWorkspace(db, store, snapshot)).
|
|
205
|
+
return cached?.reports ?? (await loadTeamWorkspace(db, store, snapshot)).reports
|
|
206
|
+
}
|
|
207
|
+
// The workspace's reports' file trees, by report ID (withoutTree), for the
|
|
208
|
+
// same snapshot as its reports and with the same recheck before sending.
|
|
209
|
+
export async function loadTeamTreesResponse(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot): Promise<Buffer> {
|
|
210
|
+
const cached = reportCaches.get(db)?.get(workspaceKey(snapshot))
|
|
211
|
+
return cached?.trees ?? (await loadTeamWorkspace(db, store, snapshot)).trees
|
|
175
212
|
}
|
|
176
213
|
export async function loadTeamReports(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot): Promise<TeamReport[]> {
|
|
177
214
|
// Object consumers get their own copy, including when sharing a cold load.
|
|
@@ -192,6 +229,19 @@ export async function teamWorkspaceFindingIds(db: ManagedDb, store: BlobStore, s
|
|
|
192
229
|
for (const report of (await teamVisibility(db, store, snapshot)).values()) for (const id of report.ids) ids.add(id)
|
|
193
230
|
return ids
|
|
194
231
|
}
|
|
232
|
+
|
|
233
|
+
// What a focused team's annotations are at, for the snapshot reading them, as
|
|
234
|
+
// its feed tells it (team-feed.ts serveUserTeamFeed) and its annotations
|
|
235
|
+
// answer it (loadTeamAnnotations): the snapshot's key and the revision of
|
|
236
|
+
// every visible finding's annotations (getAnnotationRevision), its workspace's
|
|
237
|
+
// findings being `ids` (teamWorkspaceFindingIds).
|
|
238
|
+
export async function teamAnnotationRevision(db: ManagedDb, snapshot: TeamReportAccessSnapshot, ids: readonly string[]): Promise<string> {
|
|
239
|
+
return JSON.stringify([teamSnapshotKey(snapshot), await db.getAnnotationRevision(ids)])
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// A revision as the client is told it: a digest, telling only whether what it
|
|
243
|
+
// covers changed, none of the finding IDs, batches or issues it is read from.
|
|
244
|
+
export const annotationRevisionDigest = (revision: string): string => createHash('sha256').update(revision).digest('base64url')
|
|
195
245
|
export async function teamReportVisibility(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot, reportId: string): Promise<ReportVisibility> {
|
|
196
246
|
if (!snapshot.reports.some(r => r.id === reportId)) throw new TeamReportsError(404, 'no-report')
|
|
197
247
|
const visible = await teamVisibility(db, store, snapshot)
|
|
@@ -218,18 +268,25 @@ export async function teamSourcePaths(db: ManagedDb, store: BlobStore, sessionId
|
|
|
218
268
|
|
|
219
269
|
// Serialize shared annotation bodies once. Reports carry only the annotated
|
|
220
270
|
// finding IDs visible in that report; callers revalidate access before sending.
|
|
271
|
+
// With them, the digest of the revision the whole workspace's are at, as the
|
|
272
|
+
// team's feed tells it (`revision`), read before them: a write between leaves
|
|
273
|
+
// it older than what is sent, so the feed's next differs and the client reads
|
|
274
|
+
// again, never the reverse.
|
|
221
275
|
export async function loadTeamAnnotations(db: ManagedDb, store: BlobStore, snapshot: TeamReportAccessSnapshot, reportId: string | null = null) {
|
|
222
276
|
// Restrict annotation reads only after classifying the complete workspace:
|
|
223
277
|
// links and findings in other reports can affect this report's visibility.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
278
|
+
if (reportId !== null && !snapshot.reports.some(r => r.id === reportId)) throw new TeamReportsError(404, 'no-report')
|
|
279
|
+
const workspace = await teamVisibility(db, store, snapshot)
|
|
280
|
+
const visible = reportId === null ? workspace : new Map([[reportId, workspace.get(reportId)!]])
|
|
281
|
+
const all = new Set<string>(), ids = new Set<string>()
|
|
227
282
|
for (const report of visible.values()) for (const id of report.ids) ids.add(id)
|
|
283
|
+
for (const report of workspace.values()) for (const id of report.ids) all.add(id)
|
|
284
|
+
const revision = await teamAnnotationRevision(db, snapshot, [...all])
|
|
228
285
|
const { triage, comments, issues: savedIssues } = await db.getAnnotations([...ids])
|
|
229
286
|
const issues = Object.fromEntries(visibleManagedIssues(savedIssues, snapshot)
|
|
230
287
|
.map(issue => [issue.findingId, { url: issue.issueUrl!, autoFix: issue.autoFixUrl }]))
|
|
231
288
|
const entries = Object.fromEntries(triage.map(row => [row.findingId, triageWireEntry(row)]))
|
|
232
289
|
const annotated = new Set([...triage.map(row => row.findingId), ...comments.map(comment => comment.findingId), ...Object.keys(issues)])
|
|
233
290
|
const reports = Object.fromEntries([...visible].map(([id, report]) => [id, [...report.ids].filter(finding => annotated.has(finding))]))
|
|
234
|
-
return { reports, entries, comments, issues }
|
|
291
|
+
return { reports, entries, comments, issues, revision: annotationRevisionDigest(revision) }
|
|
235
292
|
}
|
|
@@ -45,25 +45,45 @@ export async function putUploadPart(store: BlobStore, session: string, kind: Upl
|
|
|
45
45
|
await store.put(partId(session, kind, id, index), bytes)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
// Parts read at once while an upload is put together.
|
|
49
|
+
const PARTS_READ_AT_ONCE = 6
|
|
50
|
+
|
|
51
|
+
// The staged upload `req` names, its parts read a few at a time into the one
|
|
52
|
+
// buffer it fills, each as long as its place in it. Its parts are deleted
|
|
53
|
+
// once read, or once it fails: awaited, unless `behind` takes that cleanup to
|
|
54
|
+
// go on past the response (http.ts), which staging maintenance backs up.
|
|
55
|
+
export async function readUpload(store: BlobStore, req: IncomingMessage, session: string, kind: UploadKind, maxBytes: number,
|
|
56
|
+
behind?: (cleanup: Promise<void>) => void): Promise<Buffer> {
|
|
49
57
|
const id = req.headers['x-upload-id']
|
|
50
58
|
const count = Number(req.headers['x-upload-parts']), size = Number(req.headers['x-upload-size'])
|
|
51
59
|
if (typeof id !== 'string' || !validUpload(id, count)) throw new Error('bad-upload')
|
|
52
60
|
try {
|
|
53
61
|
if (!Number.isSafeInteger(size) || size <= 0 || count !== Math.ceil(size / UPLOAD_CHUNK_BYTES)) throw new Error('bad-upload')
|
|
54
62
|
if (size > maxBytes || size > MAX_STAGED_BYTES) throw new Error('too-large')
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
// Every byte of it is written below before it is returned: each part
|
|
64
|
+
// must fill its place exactly, or the upload fails.
|
|
65
|
+
const body = Buffer.allocUnsafe(size)
|
|
66
|
+
let failed = false, next = 0
|
|
67
|
+
const readParts = async () => {
|
|
68
|
+
while (!failed && next < count) {
|
|
69
|
+
const index = next++
|
|
70
|
+
const bytes = await store.get(partId(session, kind, id, index))
|
|
71
|
+
const expected = Math.min(UPLOAD_CHUNK_BYTES, size - index * UPLOAD_CHUNK_BYTES)
|
|
72
|
+
if (!bytes || bytes.length !== expected) { failed = true; throw new Error('bad-upload') }
|
|
73
|
+
bytes.copy(body, index * UPLOAD_CHUNK_BYTES)
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
|
-
|
|
76
|
+
// Each read settled before its parts are deleted.
|
|
77
|
+
const results = await Promise.allSettled(Array.from({ length: Math.min(PARTS_READ_AT_ONCE, count) }, readParts))
|
|
78
|
+
const rejected = results.find(result => result.status === 'rejected')
|
|
79
|
+
if (rejected) throw rejected.reason
|
|
80
|
+
return body
|
|
63
81
|
} finally {
|
|
64
82
|
// These bytes can no longer be finalized. Failed/abandoned chunk uploads
|
|
65
83
|
// are also covered by automatic staging maintenance.
|
|
66
|
-
|
|
84
|
+
const cleanup = deleteUpload(store, session, kind, id, count).catch(err => console.warn('managed: upload cleanup failed:', err))
|
|
85
|
+
if (behind) behind(cleanup)
|
|
86
|
+
else await cleanup
|
|
67
87
|
}
|
|
68
88
|
}
|
|
69
89
|
|
package/out/chunk-4UFAMJAX.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{z as e}from"./chunk-S6GIXWQW.js";import{m as t,p as n,u as r}from"./chunk-JKGQ3O7O.js";import{a as i}from"./chunk-3PS4KWD2.js";import{b as a}from"./chunk-IYPY47MC.js";import{D as o,J as s,L as c,q as l}from"./chunk-SXV2T5C4.js";function u(e){let{data:t}=c(e);return t&&d(t)>0?t:null}function d(e){return(s(e)??[]).filter(t=>(Array.isArray(t)?t:[t]).some(t=>t&&typeof t==`object`&&l(t,t.source??e.source))).length}function f(e,t){let n=[],r=new Map;for(let{name:t,content:i,fallbackRepo:a}of e){if(typeof i!=`string`)continue;let e=u(i);if(!e)continue;let c=s(e),l=o(e)??o({repo:{github:a}}),f=new Set(l?[l]:[]);if(!l)for(let e of c.flat()){if(/(?:^|\/)(?:node_modules|dependencies)\//u.test(e?.file??``))continue;let t=o(e);t&&f.add(t)}for(let e of f)r.set(e,{id:e,label:e});n.push({id:t,filename:t,repoIds:[...f],analyzer:e.source,appFindings:d(e)})}let i=new Set(n.map(e=>e.id));return{merge:{bundles:[],results:[]},link:{reports:n,repositories:[...r.values()],workspaces:t.map(e=>({id:e.id,label:e.name,reports:e.reports.filter(e=>i.has(e))})).filter(e=>e.reports.length>0)}}}function p(e,t){let n=(e.reports??[]).filter(e=>e.visible&&e.repoId!=null).flatMap(e=>{let t=u(e.content??``);return t?[{id:e.id,filename:e.filename,repoIds:[e.repoId],repo:e.repoFullName,directory:e.repoDirectory,analyzer:e.analyzer,appFindings:d(t)}]:[]}),r=new Map((e.repos??[]).map(e=>[e.repoId,{id:e.repoId,label:e.fullName}]));for(let e of n)r.has(e.repoIds[0])||r.set(e.repoIds[0],{id:e.repoIds[0],label:e.repo??String(e.repoIds[0])});let a=t.results??[],o=new Set(a.map(e=>e.bundleId));return{merge:{bundles:(t.bundles??[]).filter(e=>o.has(e.id)).map(e=>({...e,size:i(e.byteSize)})),results:a},link:{repositories:[...r.values()].filter(e=>n.some(t=>t.repoIds.includes(e.id))),reports:n}}}var m=new Map([[`openai/gpt-6-astra`,`GPT-6 Astra`],[`openai/gpt-6-astra-pro`,`GPT-6 Astra Pro`],[`openai/gpt-5.6-sol`,`GPT-5.6 Sol`],[`openai/gpt-5.6-terra`,`GPT-5.6 Terra`],[`openai/gpt-5.6-luna`,`GPT-5.6 Luna`],[`anthropic/claude-fable-5.1`,`Claude Fable 5.1`],[`anthropic/claude-opus-5`,`Claude Opus 5`],[`anthropic/claude-sonnet-5`,`Claude Sonnet 5`],[`moonshotai/kimi-k3`,`Kimi K3`],[`z-ai/glm-5.3`,`GLM 5.3`],[`qwen/qwen3.8-max`,`Qwen3.8 Max`],[`deepseek/deepseek-v4.1-flash`,`DeepSeek V4.1 Flash`],[`deepseek/deepseek-v4-pro`,`DeepSeek V4 Pro`]]),h=new Map([[`openai`,`OpenAI`],[`anthropic`,`Anthropic`],[`moonshotai`,`Moonshot AI`],[`google`,`Google`],[`nvidia`,`NVIDIA`],[`qwen`,`Qwen`],[`deepseek`,`DeepSeek`],[`x-ai`,`xAI`],[`z-ai`,`Z.ai`],[`mistralai`,`Mistral AI`],[`meta`,`Meta`],[`unbiased`,`Unbiased`]]),g=new Map([[`meta-llama`,`meta`]]),_=new Map([[`gpt`,`GPT`],[`gptoss`,`GPT OSS`],[`oss`,`OSS`],[`o`,`O`],[`ai`,`AI`],[`api`,`API`],[`4o`,`4o`],[`k3`,`K3`],[`k4`,`K4`],[`qwen`,`Qwen`],[`kimi`,`Kimi`],[`mimo`,`MiMo`],[`gemini`,`Gemini`],[`claude`,`Claude`],[`fable`,`Fable`],[`opus`,`Opus`],[`sonnet`,`Sonnet`],[`astra`,`Astra`],[`terra`,`Terra`],[`luna`,`Luna`],[`codex`,`Codex`],[`deepseek`,`DeepSeek`],[`nemotron`,`Nemotron`],[`gemma`,`Gemma`]]);function v(e){let t=e.replace(/:free$/u,``).replaceAll(`_`,`-`).split(`-`).filter(Boolean),n=[];for(let e=0;e<t.length;e++){let r=t[e],i=t[e+1];if(/^\d+$/u.test(r)&&/^\d+$/u.test(i??``)){n.push(`${r}.${i}`),e++;continue}let a=_.get(r.toLowerCase());n.push(a??(/^(?:[a-z]+\d+|\d+[a-z]+)$/iu.test(r)?r.toUpperCase():r[0]?.toUpperCase()+r.slice(1)))}return n.join(` `).replace(/^GPT (\d)/u,`GPT-$1`)}function y(e){return m.get(e)??v(e.includes(`/`)?e.slice(e.indexOf(`/`)+1):e)}function b(e){let t=e.includes(`/`)?e.split(`/`)[0].replace(/^~/u,``):`other`,n=g.get(t)??t;return{key:n,name:h.get(n)??(n===`other`?`Other`:v(n))}}function x(e){return e===`xhigh`?`Extra high`:e?e[0].toUpperCase()+e.slice(1):`Unavailable`}function S(e){return e.efforts.includes(`max`)?`max`:e.efforts.at(-1)??null}async function C(e,t=fetch){let n=await t(`/api/admin/scan/models`,{credentials:`same-origin`,headers:{accept:`application/json`},signal:e});if(!n.ok)throw Error(`HTTP ${n.status}`);return w(await n.json())}function w(e){if(!Array.isArray(e?.models))throw Error(`No model catalogue returned`);let t=e.models.filter(e=>typeof e?.id==`string`&&e.id.length>0).map(e=>({id:e.id,efforts:Array.isArray(e.efforts)?[...new Set(e.efforts.filter(e=>typeof e==`string`&&e.length>0))]:[]}));if(t.length===0)throw Error(`No models available`);return{models:t,defaultModel:t.find(t=>t.id===e.defaultModel)?.id??t[0].id}}function T(e,t=[]){let n=new Map,r=e.flatMap(e=>{let r=t.filter(t=>t.bundles?.includes(e.integrity));return(r.length>0?r.map(e=>({id:e.id,label:e.name})):[{id:`unattached`,label:`Unattached`}]).map(t=>(n.set(t.id,t),{id:e.integrity,integrity:e.integrity,filename:e.name,repoId:t.id,repo:t.label,files:null,reasons:[],size:`—`}))});return{repositories:[...n.values()],bundles:r,reports:[],scans:[]}}function E(o,s){if(s.error)throw Error(s.error);let c=n(s),l=s.kind===`stasis`?s.bundle?.formats:null,u=r(s),d=[...t(s)].filter(([,e])=>e!=null).map(([e,t])=>({path:e,format:l?.get(e)??null,bytes:t,lines:u.get(e)??0,size:i(t),module:a(e,{packageDir:c?.get(e)})})),f=[{id:`all`,label:`All`,filePaths:null},...[...e(s,d.map(e=>e.path))].map(([e,t])=>({id:`reason:${e}`,label:e,filePaths:[...t]}))];return{...o,files:d,reasons:f,size:i(s.size)}}export{T as a,E as b,f as c,p as d,y as e,b as f,x as g,S as h,C as i,w as j};
|
package/out/chunk-BOIXMUU5.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./chunk-QB4CBOX2.js";import{a as t,b as n,c as r,f as i,g as a}from"./chunk-S6GIXWQW.js";import{i as o}from"./chunk-BC2UCLFJ.js";import{J as s}from"./chunk-SXV2T5C4.js";import{a as c}from"./chunk-TUII34K2.js";var l=[`dependencies`,`security`],u={dependencies:`Dependencies`,security:`Security`},d=e(),f=Object.freeze(d.models),p=d.defaultModel,m=`x-upload-encryption`,h=1,g=1,_={name:`X25519`},ee=34,v=1048576,y=65536,b=`deepview-managed-upload.v1.content-key`;function x(e,t){let n=new Uint8Array(12);return new DataView(n.buffer).setUint32(7,e),n[11]=+!!t,n}async function S(e,t,n){let r=await crypto.subtle.importKey(`raw`,e,`HKDF`,!1,[`deriveKey`]);return await crypto.subtle.deriveKey({name:`HKDF`,hash:`SHA-256`,salt:t,info:c(b)},r,{name:`AES-GCM`,length:256},!1,[n])}async function te(e){let t=new Uint8Array(await e.slice(0,y).arrayBuffer());if(t.includes(0))return!1;try{return new TextDecoder(`utf-8`,{fatal:!0}).decode(t,{stream:!0}),!0}catch{return!1}}async function C(e,t,n){let r=await te(e),[i,a]=await Promise.all([crypto.subtle.importKey(`raw`,t,_,!1,[]),crypto.subtle.generateKey(_,!1,[`deriveBits`])]),o=new Uint8Array(ee);o[0]=h,o[1]=r?g:0,o.set(new Uint8Array(await crypto.subtle.exportKey(`raw`,a.publicKey)),2);let s=await S(await crypto.subtle.deriveBits({name:`X25519`,public:i},a.privateKey,256),o,`encrypt`),c=[new Blob([o])],l=new Uint8Array(v),u=0,d=0,f=async e=>{c.push(new Blob([await crypto.subtle.encrypt({name:`AES-GCM`,iv:x(d++,e)},s,l.subarray(0,u))])),u=0},p=(r?e.stream().pipeThrough(new CompressionStream(`gzip`)):e.stream()).getReader();try{for(let e=await p.read();!e.done;e=await p.read()){n?.throwIfAborted();for(let t=0;t<e.value.length;){u===v&&await f(!1);let n=Math.min(v-u,e.value.length-t);l.set(e.value.subarray(t,t+n),u),u+=n,t+=n}}}catch(e){throw await p.cancel(e).catch(()=>{}),e}return await f(!0),new Blob(c)}var w=null,T=0;function E(){return w?.user.role??null}function D(e){if(e!==null&&!n(e))throw TypeError(`Unknown managed preview role: ${e}. Expected ${t.join(`, `)}.`);T++,w=e===null?null:{user:{id:`preview:${e}`,login:e,name:e[0].toUpperCase()+e.slice(1),role:e,avatarUrl:null,lastSeenAt:Date.now(),lastActivityAt:null},csrfToken:null}}function O(e,t){let n=new URL(e,`http://managed-preview.invalid`).pathname,i=(t?.method??`GET`).toUpperCase();if(i===`POST`&&n===`/api/auth/logout`)return Response.json({ok:!0});if(i!==`GET`)throw Error(`Changes are not saved in the managed UI preview`);if(n===`/api/auth/session`)return Response.json(w);if(n===`/api/teams`)return Response.json({teams:[]});if(n.startsWith(`/api/admin/`)&&!r(w.user.role,`manage`)||[`/api/admin/users`,`/api/admin/teams`,`/api/admin/repositories`,`/api/admin/repositories/aliases`,`/api/admin/repositories/risk`].includes(n)&&w.user.role!==`admin`)return Response.json({error:`forbidden`},{status:403});if(n===`/api/admin/repositories/risk`)return new Response(`{"type":"repos","repos":[]}
|
|
2
|
-
{"type":"done"}
|
|
3
|
-
`,{headers:{"content-type":`application/x-ndjson`}});let a={"/api/admin/users":{users:[w.user]},"/api/admin/teams":{teams:[],users:[w.user],repos:[],permissions:l},"/api/admin/links":{shares:[]},"/api/admin/repositories":{repositories:[],total:0,connectedCount:0,installUrl:null,tokenMissing:!1},"/api/admin/repositories/aliases":{aliases:[],repos:[]},"/api/admin/repositories/resolve":{location:null},"/api/admin/reports":{reports:[],repos:[],maxBytes:26214400},"/api/admin/bundles":{bundles:[],repos:[],maxBytes:209715200},"/api/admin/history":{history:[],total:0,page:1,limit:100,filters:{repos:[],users:[]}},"/api/admin/scan/models":{models:f,defaultModel:p},"/api/admin/scan-results":{bundles:[],results:[]},"/api/auth/profile":{profile:{login:w.user.login,name:w.user.name,githubName:w.user.name,nameAccount:null,avatarAccount:null,accounts:[]}}};return Object.hasOwn(a,n)?Response.json(a[n]):Response.json({error:`not-found`},{status:404})}var k=3145728,A=new Set([`/api/admin/reports`,`/api/admin/bundles`,`/api/admin/deduplication`]);async function j(e,t){let n=await t(`/api/admin/uploads/key`,{credentials:`same-origin`,signal:e.signal});if(n.status===404||n.status===405)return e;if(!n.ok)return n;let r=(await n.json())?.key;if(typeof r!=`string`)throw Error(`The server sent an invalid upload key`);let i=new Headers(e.headers);return i.set(m,`1`),{...e,headers:i,body:await C(e.body,Uint8Array.fromBase64(r,{alphabet:`base64url`}),e.signal)}}async function M(e,t,n){let r=null;if(e!==`/api/admin/deduplication`&&t.body.size>k){let i=await n(`/api/config`,{credentials:`same-origin`,signal:t.signal});if(!i.ok)return i;let a=(await i.json()).managed,o=a?.uploadMaxBytes?.[e.endsWith(`/reports`)?`reports`:`bundles`];if(Number.isSafeInteger(o)&&o>0&&t.body.size>o)return Response.json({error:`too-large`},{status:413});let s=a?.uploadChunkBytes;Number.isSafeInteger(s)&&s>0&&s<=k&&(r=s)}let i=await j(t,n);return i instanceof Response?i:r==null||i.body.size<=k?await n(e,i):await N(e,i,n,r)}async function N(e,t,n,r){let i=t.body,a=crypto.randomUUID(),o=e.endsWith(`/reports`)?`reports`:`bundles`,s=Math.ceil(i.size/r),c=new Headers(t.headers);c.set(`content-type`,`application/octet-stream`);let l=0,u=!1;try{for(let e=0;e<s;e++){l=e+1;let s=await n(`/api/admin/uploads/${o}/${a}/${e}`,{...t,headers:c,body:i.slice(e*r,(e+1)*r)});if(!s.ok)return s}let d=new Headers(t.headers);d.set(`x-upload-id`,a),d.set(`x-upload-parts`,String(s)),d.set(`x-upload-size`,String(i.size));let f=await n(e,{...t,headers:d,body:``});return u=f.ok,f}finally{if(!u&&l>0){let e=new Headers(t.headers);e.set(`x-upload-parts`,String(l)),await n(`/api/admin/uploads/${o}/${a}`,{...t,method:`DELETE`,headers:e,body:void 0,signal:AbortSignal.timeout(1e4)}).catch(()=>{})}}}async function P(e){await e.json().then(e=>e?.error,()=>null)===`view-only`&&globalThis.document?.dispatchEvent(new CustomEvent(`managed-view-only`))}async function F(e,t){if(t?.signal?.throwIfAborted(),w)return O(e,t);let n=T;async function r(e,t){if(n!==T)throw new DOMException(`Managed session changed`,`AbortError`);let r=i();if(r){if(new URL(e,globalThis.location.origin).origin!==globalThis.location.origin)throw Error(`Public workspace requests must be same-origin`);let n=new Headers(t?.headers);n.set(`x-deepview-share`,r.token),t={...t,headers:n,credentials:`omit`,redirect:`error`}}let a=await fetch(e,{...t,cache:`no-store`});if(n!==T)throw new DOMException(`Managed session changed`,`AbortError`);return a.status===403&&![`GET`,`HEAD`].includes((t?.method??`GET`).toUpperCase())&&P(a.clone()),a}return t?.method===`POST`&&A.has(e)&&t.body instanceof Blob?await M(e,t,r):await r(e,t)}var I=new class{constructor(e=()=>{}){this.resources=new Map,this.session=null,this.teamCatalog=null,this.reportCatalog=null,this.bundleCatalog=null,this.generation=0,this.sessionController=new AbortController,this.notify=e}setSession(e){(this.session?.id!==e?.id||this.session?.role!==e?.role)&&this.reset(),this.session=e}reset(){this.generation++,this.sessionController.abort(),this.sessionController=new AbortController,this.invalidate(),this.session=null,this.teamCatalog=null,this.reportCatalog=null,this.bundleCatalog=null}setReportCatalog(e){let t=new Set,n=new Map(e.map(e=>[e.id,JSON.stringify([e.cacheKey??null,e.reports.map(e=>[e.id,e.cacheKey??null,e.visible!==!1]).toSorted(),(e.bundles??[]).map(e=>[e.id,e.filename,e.repoFullName,e.repoDirectory??``]).toSorted()])]));for(let e of new Set([...this.teamCatalog?.keys()??[],...n.keys()]))this.teamCatalog?.get(e)!==n.get(e)&&t.add(`team:${e}`);for(let e of this.resources.keys())if(e.startsWith(`reports:content:team:`)){let[t]=e.slice(21).split(`:`);(!n.has(t)||this.teamCatalog?.get(t)!==n.get(t))&&this.invalidate([e])}this.teamCatalog=n;let r=new Map;for(let t of e)for(let e of t.reports){let n=r.get(e.id)??[];n.push(JSON.stringify([t.id,e.cacheKey??null])),r.set(e.id,n)}let i=new Map([...r].map(([e,t])=>[e,JSON.stringify(t.toSorted())])),a=this.reportCatalog;this.reportCatalog=i;let o=new Set(t);for(let e of new Set([...a?.keys()??[],...i.keys()]))a?.get(e)!==i.get(e)&&o.add(e);for(let e of this.resources.keys()){if(!e.startsWith(`reports:content:`)||e.startsWith(`reports:content:team:`))continue;let t=e.slice(16);(!i.has(t)||o.has(t))&&(this.invalidate([e]),o.add(t))}let s=new Map;for(let t of e)for(let e of t.bundles??[]){let n=s.get(e.id)??[];n.push(JSON.stringify([t.id,t.cacheKey??null,e.filename,e.repoFullName,e.repoDirectory??``])),s.set(e.id,n)}let c=!1,l=new Map([...s].map(([e,t])=>[e,JSON.stringify(t.toSorted())]));for(let e of new Set([...this.bundleCatalog?.keys()??[],...l.keys()]))this.bundleCatalog?.get(e)!==l.get(e)&&(c=!0,o.add(`bundle:${e}`),this.invalidate([`bundle-metadata:${e}`]));this.bundleCatalog=l;for(let e of this.resources.keys()){if(!e.startsWith(`report-sources:`)&&!e.startsWith(`finding-history:`))continue;let[,n,r]=e.split(`:`);(n?t.has(`team:${n}`):c||o.has(r))&&this.invalidate([e])}return o}read(e){return this.resources.get(e)?.data}invalidate(e){for(let[t,n]of this.resources)e&&!e.some(e=>t===e||t.startsWith(`${e}:`))||(n.controller?.abort(),this.resources.delete(t))}async mutate(e,t){let n=this.generation,r;try{r=await e()}catch(e){throw n===this.generation&&e?.name!==`AbortError`&&this.notify(`Couldn't save changes: ${e?.message??e}`),e}if(n!==this.generation)throw new DOMException(`Managed session changed`,`AbortError`);return this.invalidate(t),r}async load(e,t,n,{signal:r,apply:i=()=>{},retryInvalidated:a=!1}={}){let o=this.generation;for(;;){r?.throwIfAborted();let s=this.resources.get(e);if(s||(s={},this.resources.set(e,s)),s.data!==void 0&&i(s.data),!s.pending){let e=s.controller=new AbortController;s.pending=(async()=>{try{let t=await n(e.signal);return e.signal.throwIfAborted(),s.data=t,t}catch(n){throw!e.signal.aborted&&n?.name!==`AbortError`&&this.notify(`Couldn't refresh ${t}: ${n?.message??n}`),n}})().finally(()=>{s.pending=null})}try{let t=await s.pending;if(r?.throwIfAborted(),this.resources.get(e)!==s)throw new DOMException(`Managed data changed`,`AbortError`);return i(t),t}catch(t){if(!a||t?.name!==`AbortError`||r?.aborted||o!==this.generation||this.resources.get(e)===s)throw t}}}}(e=>{document.dispatchEvent(new CustomEvent(`managed-notice`,{detail:{message:e}}))});function L(e){I.setSession(e)}function R(e){return I.setReportCatalog(e)}function z(){I.reset()}async function B(e,t=null,n={}){let r;try{r=await F(e,{credentials:`same-origin`,headers:{accept:`application/json`},...n})}catch{return t}if(r.status===401||r.status===403)return null;if(!r.ok)return t;try{return await r.json()}catch{return t}}async function V({fallback:e=null,sent:t}={}){let n=i()||E()!==null?void 0:await t;if(n===null)return null;let r=H(n);if(r!==void 0)return r;let o;try{let t=i(),n=await F(t?a(t):`/api/auth/session`,{credentials:`same-origin`,headers:{accept:`application/json`}});if(n.status===401||n.status===403)return null;if(!n.ok)return e;o=await n.json()}catch{return e}let s=H(o);return s===void 0?e:s}function H(e){let t=e?.user;if(t===null)return null;if(t!==void 0&&typeof t.login==`string`)return{id:t.id,login:t.login,name:typeof t.name==`string`?t.name:null,avatarUrl:typeof t.avatarUrl==`string`?t.avatarUrl:null,role:typeof t.role==`string`?t.role:`none`,csrfToken:typeof e.csrfToken==`string`?e.csrfToken:null,...i()?{publicShare:!0}:{},...typeof e.viewer?.login==`string`?{viewer:{id:e.viewer.id,login:e.viewer.login,name:typeof e.viewer.name==`string`?e.viewer.name:null}}:{},...!i()&&e.dependencyReview===!0?{dependencyReview:!0}:{}}}function U(e){let t=t=>Number.isSafeInteger(e?.[t])&&e[t]>=0;return e?.appMode===!1?{appMode:!1,...t(`appReports`)&&{appReports:e.appReports},...t(`findings`)&&{findings:e.findings}}:e?.appMode===!0&&t(`appFindings`)?{appMode:!0,appFindings:e.appFindings}:null}function W(e){if(typeof e?.sha!=`string`||typeof e.github!=`string`||!Array.isArray(e.tags))return null;let t=e.details,n=e=>typeof e==`string`?e:null,r=e=>Number.isSafeInteger(e)?e:null;return{sha:e.sha,github:e.github,tags:e.tags.filter(e=>typeof e==`string`),details:typeof t?.subject==`string`?{subject:t.subject,authorName:n(t.authorName),authorLogin:n(t.authorLogin),authoredAt:r(t.authoredAt),committedAt:r(t.committedAt)}:null}}async function G({fallback:e=[],signal:t,onRevision:n}={}){let r=i(),o=r?await B(a(r),null,{signal:t}):null,s=r?{teams:o?.team?[o.team]:[]}:await B(`/api/teams`,{teams:e},{signal:t}),c=s?.teams;return Array.isArray(c)?(n?.(typeof s.revision==`string`?s.revision:null),c.filter(e=>e!=null&&typeof e.id==`string`&&typeof e.name==`string`).map(e=>({id:e.id,slug:e.slug,name:e.name,...!r&&e.dependencyReview===!0?{dependencyReview:!0}:{},...e.permissions?{permissions:{dependencies:e.permissions.dependencies===!0,security:e.permissions.security===!0}}:{},...typeof e.cacheKey==`string`?{cacheKey:e.cacheKey}:{},...U(e.app)?{app:U(e.app)}:{},repos:Array.isArray(e.repos)?e.repos.filter(e=>Number.isSafeInteger(e?.repoId)&&e.repoId>0&&typeof e.fullName==`string`).map(e=>({repoId:e.repoId,fullName:e.fullName,path:typeof e.path==`string`&&e.path?e.path:null})):[],reports:Array.isArray(e.reports)?e.reports.filter(e=>e!=null&&typeof e.id==`string`&&typeof e.filename==`string`).map(e=>({id:e.id,slug:e.slug,filename:e.filename,visible:e.visible!==!1,...e.analyzer===null||typeof e.analyzer==`string`?{analyzer:e.analyzer}:{},repoFullName:typeof e.repoFullName==`string`?e.repoFullName:``,repoDirectory:typeof e.repoDirectory==`string`?e.repoDirectory:``,...typeof e.cacheKey==`string`?{cacheKey:e.cacheKey}:{}})):[],bundles:Array.isArray(e.bundles)?e.bundles.filter(e=>e!=null&&typeof e.id==`string`&&typeof e.filename==`string`).map(e=>({id:e.id,slug:e.slug,integrity:e.integrity,byteSize:e.byteSize,repoId:e.repoId,filename:e.filename,kind:e.kind,visible:e.visible!==!1,summary:e.summary??null,summaryRetryAt:Number.isSafeInteger(e.summaryRetryAt)&&e.summaryRetryAt>0?e.summaryRetryAt:null,repoDirectory:typeof e.repoDirectory==`string`?e.repoDirectory:``,repoFullName:typeof e.repoFullName==`string`?e.repoFullName:``,commitInfo:W(e.commitInfo)})):[]}))):s==null?[]:e}function K(e){return e?.links===void 0?[]:Array.isArray(e.links)&&e.links.every(e=>Array.isArray(e)&&e.length>=2&&e.every(e=>typeof e==`string`))?e.links:null}function q(e){return s(e?.data)===null||typeof e.repo?.directory!=`string`||e.repo.github!==null&&typeof e.repo.github!=`string`||K(e)===null?null:{data:e.data,repo:e.repo,...e.links?{links:e.links}:{}}}async function J(e,{signal:t}={}){return q(await B(`/api/reports/${encodeURIComponent(e)}`,null,{signal:t}))}async function Y(e,{signal:t}={}){let n=[...new Set(e)];if(n.length===0)return[];let r=await B(`/api/reports/query`,null,{method:`POST`,signal:t,headers:{accept:`application/json`,"content-type":`application/json`},body:JSON.stringify({ids:n})});if(!Array.isArray(r?.reports)||r.reports.length!==n.length)return null;let i=new Map;for(let e of r.reports){let t=q(e);if(!t||!n.includes(e.id)||i.has(e.id))return null;i.set(e.id,t)}let a=K(r);return a===null?null:e.map(e=>({...i.get(e),...a.length>0?{links:a}:{}}))}async function X(e,{signal:t,reportId:n=null}={}){let r=n===null?``:`?reportId=${encodeURIComponent(n)}`,i=await B(`/api/teams/${encodeURIComponent(e)}/reports${r}`,null,{signal:t});if(!Array.isArray(i?.reports))return null;let a=[],o=new Set;for(let e of i.reports){let t=q(e);if(!t||typeof e.id!=`string`||typeof e.filename!=`string`||o.has(e.id))return null;o.add(e.id),a.push({id:e.id,filename:e.filename,...t})}let s=K(i);return s===null?null:(Object.defineProperty(a,"links",{value:s}),a)}function Z(e){return e?`?team=${encodeURIComponent(e)}`:``}async function Q(e,{reportId:t,...n}={}){let r=t==null?``:`?reportId=${encodeURIComponent(t)}`,{reports:i,entries:a,comments:o,issues:s={}}=await B(`/api/teams/${encodeURIComponent(e)}/annotations${r}`,null,n)??{};if(!i||typeof i!=`object`||Array.isArray(i)||!a||typeof a!=`object`||Array.isArray(a)||!Array.isArray(o)||!s||typeof s!=`object`||Array.isArray(s)||Object.values(i).some(e=>!Array.isArray(e)||e.some(e=>typeof e!=`string`))||o.some(e=>!e||typeof e.findingId!=`string`))return null;let c=new Map;for(let[e,t]of o.entries()){let n=t.findingId,r=c.get(n)??[];r.push(e),c.set(n,r)}let l=new Map,u=new Map;return e=>{if(!Object.hasOwn(i,e))return null;if(u.has(e))return u.get(e);let t=JSON.stringify(i[e]),n=l.get(t);if(!n){let r=[...new Set(i[e])],u=r.flatMap(e=>c.get(e)??[]);n={entries:Object.fromEntries(r.filter(e=>Object.hasOwn(a,e)).map(e=>[e,a[e]])),comments:u.toSorted((e,t)=>e-t).map(e=>o[e]),issues:Object.fromEntries(r.filter(e=>Object.hasOwn(s,e)).map(e=>[e,s[e]]))},l.set(t,n)}return u.set(e,n),n}}async function ne(e,t,n){let r=(await B(`/api/reports/${encodeURIComponent(e)}/triage${Z(t)}`,null,n))?.entries;return typeof r==`object`&&r&&!Array.isArray(r)?r:null}async function re(e,t,n=null){if(i())return[];let r=n===null?``:`?reportId=${encodeURIComponent(n)}`,a=await B(`/api/teams/${encodeURIComponent(e)}/fixes${r}`,null,{signal:t});return Array.isArray(a?.fixes)?a.fixes:null}async function ie(e,t,n){let r=await B(`/api/reports/${encodeURIComponent(e)}/comments${Z(t)}`,null,n);return Array.isArray(r?.comments)?r.comments:null}async function ae(e,{findingId:t,body:n,commentId:r=null,version:i},a,o){let s=r==null?``:`/${encodeURIComponent(r)}`;try{let c=await F(`/api/reports/${encodeURIComponent(e)}/comments${s}${Z(o)}`,{method:r==null?`POST`:`PATCH`,credentials:`same-origin`,headers:{"content-type":`application/json`,...a?{"x-csrf-token":a}:{}},body:JSON.stringify(r==null?{findingId:t,body:n}:{body:n,version:i})});return{status:c.status,comment:c.ok?(await c.json()).comment:null}}catch{return{status:0,comment:null}}}async function oe(e,t,n,r,i){try{return(await F(`/api/reports/${encodeURIComponent(e)}/comments/${encodeURIComponent(t)}${Z(i)}`,{method:`DELETE`,credentials:`same-origin`,headers:{"content-type":`application/json`,...r?{"x-csrf-token":r}:{}},body:JSON.stringify({version:n})})).status}catch{return 0}}async function $(e,t,n,r){try{return(await F(`/api/reports/${encodeURIComponent(e)}/triage${Z(r)}`,{method:`POST`,credentials:`same-origin`,headers:{"content-type":`application/json`,...n?{"x-csrf-token":n}:{}},body:JSON.stringify({entries:t})})).status}catch{return 0}}function se(e){e&&(location.href=e)}async function ce(e){if(i()){location.href=`/`;return}try{await F(`/api/auth/logout`,{method:`POST`,credentials:`same-origin`,headers:e?{"x-csrf-token":e}:{}})}catch{}location.reload()}async function le(e,t){let n=await F(`/api/auth/view-as`,{method:`POST`,credentials:`same-origin`,headers:{"content-type":`application/json`,...t?{"x-csrf-token":t}:{}},body:JSON.stringify({userId:e})});if(!n.ok)throw Error(`HTTP ${n.status}`);location.assign(`/`)}async function ue(e){try{await F(`/api/auth/view-as`,{method:`DELETE`,credentials:`same-origin`,headers:e?{"x-csrf-token":e}:{}})}catch{}location.assign(o[`admin-users`])}async function de(e){let t=await F(`/api/teams/${encodeURIComponent(e)}/share`,{credentials:`same-origin`});if(!t.ok)throw Error(`Could not load public links (${t.status})`);return(await t.json()).shares}async function fe(e,t,{id:n,revoke:r=!1,dependencies:i=!1,security:a=!1}={}){let o=n?`/${encodeURIComponent(n)}`:``,s=await F(`/api/teams/${encodeURIComponent(e)}/share${o}`,{method:r?`DELETE`:n?`PATCH`:`POST`,credentials:`same-origin`,headers:{"x-csrf-token":t,"content-type":`application/json`},...r?{}:{body:JSON.stringify({dependencies:i,security:a})}});if(!s.ok)throw Error(`Could not ${r?`revoke`:n?`update`:`create`} public link (${s.status})`);return s.json()}async function pe(e,t){let n=await F(`/api/bundles/${encodeURIComponent(e)}/download`,{credentials:`same-origin`});if(!n.ok)throw Error(`Could not download bundle (${n.status})`);let r=URL.createObjectURL(await n.blob()),i=document.createElement(`a`);i.href=r,i.download=t,i.click(),setTimeout(()=>URL.revokeObjectURL(r),6e4)}export{u as a,E as b,D as c,F as d,I as e,L as f,R as g,z as h,V as i,G as j,J as k,Y as l,X as m,Z as n,Q as o,ne as p,re as q,ie as r,ae as s,oe as t,$ as u,se as v,ce as w,le as x,ue as y,de as z,fe as A,pe as B};
|
package/out/chunk-CC4OM6TZ.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{Cb as e,Tb as t,Zb as n,rb as r,sb as i,tb as a,v as o,vb as s,wb as c,xb as l}from"./chunk-LE5ZWLG4.js";import{c as u,d}from"./chunk-MSION3G4.js";import{e as f,f as p,g as m,j as h}from"./chunk-SXV2T5C4.js";import{c as g}from"./chunk-2XUEZHIP.js";var _=`.token.comment,.token.prolog,.token.cdata{color:var(--code-comment);font-style:italic}.token.punctuation{color:var(--muted)}.token.string,.token.attr-value,.token.char,.token.regex{color:var(--code-string)}.token.number,.token.boolean,.token.constant,.token.symbol{color:var(--code-number)}.token.keyword,.token.builtin,.token.important,.token.atrule{color:var(--code-keyword)}.token.function,.token.class-name,.token.tag{color:var(--code-function)}.token.operator,.token.entity,.token.url,.token.attr-name,.token.selector{color:var(--code-operator)}.token.variable,.token.property,.token.parameter{color:var(--code-variable)}.token.annotation{color:var(--code-function)}.token.deleted{color:var(--code-deleted)}.token.inserted{color:var(--code-inserted)}.token.italic{font-style:italic}.token.bold{font-weight:700}.code-control{padding:0 .3em;margin:0 .1em;border-radius:3px;font-size:.8em;line-height:0;font-style:normal;font-weight:400;color:var(--warning);background:rgb(from var(--warning) r g b / .14);box-shadow:inset 0 0 0 1px rgb(from var(--warning) r g b / .45);white-space:nowrap;user-select:none}
|
|
2
|
-
`,v=g`<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>`,y=g`<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="1.8" y="5" width="20.4" height="14" rx="2"/><path d="M5.6 15.6V8.4l3.1 3.6 3.1-3.6v7.2"/><path d="M16.9 8.4v7.2m-2.6-2.9 2.6 2.9 2.6-2.9"/></svg>`,b=g`<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M6 9V2h12v7"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>`;function x(e,t){let n=u.find(t=>t.value===e)?.label??e;return d(e)?.includes(`partial`)?t===`exclude`?`${n} (full only)`:t===`only`?`${n} (partial only)`:n:n}function S(e){return h.filter(t=>e.has(t)).map(e=>f[e]??e).join(`, `)}function C(e=o){let t=[];if(e.filterSeverities.size>0&&t.push({key:`severity`,label:`Severity`,value:S(e.filterSeverities),clear:{filterSeverities:new Set}}),e.filterColors.size>0&&t.push({key:`marks`,label:`Marks`,value:[...e.filterColors].map(e=>m[e]??e).join(`, `),clear:{filterColors:new Set}}),e.filterSources.size===1&&t.push({key:`source`,label:`Source`,value:e.filterSources.has(`own`)?`Sources only`:`Dependencies only`,clear:{filterSources:new Set}}),e.filterAnalyzer&&t.push({key:`analyzer`,label:`Analyzer`,value:e.filterAnalyzer===r?`(none)`:e.filterAnalyzer,clear:{filterAnalyzer:``}}),e.filterModel&&t.push({key:`model`,label:`Model`,value:e.filterModel===a?`(none)`:e.filterModel,clear:{filterModel:``}}),e.filterRepo&&t.push({key:`repo`,label:`Repository`,value:e.filterRepo===i?`(no repo)`:e.filterRepo,clear:{filterRepo:``}}),d(e.filterRevalidate)&&t.push({key:`revalidate`,label:`Revalidation`,value:x(e.filterRevalidate,e.filterPartial),clear:{filterRevalidate:``,filterPartial:``}}),!e.filterRevalidate&&(e.filterConfMin>0||e.filterConfMax<10)){let n;n=e.filterConfMin>0&&e.filterConfMax<10?`${e.filterConfMin}\u2013${e.filterConfMax}`:e.filterConfMin>0?`\u2265 ${e.filterConfMin}`:`\u2264 ${e.filterConfMax}`,t.push({key:`confidence`,label:`Confidence`,value:n,clear:{filterConfMin:0,filterConfMax:10}})}e.filterInclude&&t.push({key:`search`,label:e.filterIncludeNegate?`Excluding`:`Search`,value:`"${e.filterInclude}"`,clear:{filterInclude:``,filterIncludeNegate:!1}});for(let[n,r]of[[`filterComment`,`comment`],[`filterFix`,`fix`],[`filterFlagged`,`flag`]]){let i=e[n];i&&t.push({key:`annotation:${r}`,label:`Annotation`,value:`${i===`with`?`With`:`Without`} ${r}`,clear:{[n]:``}})}return!o.currentWorkspace&&e.filterDuplicates&&t.push({key:`duplicates`,label:`Duplicates`,value:e.filterDuplicates===`with`?`Only rows linked to other reports`:`Exclude rows linked to other reports`,clear:{filterDuplicates:``}}),e.filterCrossContext&&t.push({key:`cross-context`,label:`Context`,value:e.filterCrossContext===`with`?`Only rows across multiple repos or packages`:`Exclude rows across multiple repos or packages`,clear:{filterCrossContext:``}}),e.filterAppStacked&&t.push({key:`app-stacked`,label:`App stack`,value:e.filterAppStacked===`with`?`Only rows with multiple App findings`:`Exclude rows with multiple App findings`,clear:{filterAppStacked:``}}),e.filterSecurity&&t.push({key:`security`,label:`Security`,value:e.filterSecurity===`with`?`Only security-related rows`:`Exclude security-related rows`,clear:{filterSecurity:``}}),t}function w(){return n().filter(e=>t(e).commonTriage===o.shownTriage)}function T(){return o.shownTriage?p[o.shownTriage]??o.shownTriage:null}function E(t,n=s()){let r=w(),i=r.length;c(n);let a;try{a=e(r).length}finally{l()}return{included:a,total:i,excluded:i-a,fields:n,filters:C(n),bucketLabel:T(),focusedOnly:t===`print`&&o.viewMode===`focus`}}var D=`.managed-comment{container:managed-comment / inline-size;white-space:normal}.managed-comment-row{display:grid;grid-template-columns:minmax(0,max-content) minmax(0,1fr) auto;align-items:start;gap:.35rem .65rem;line-height:1.5}.managed-comment-author{grid-column:1;grid-row:1;display:flex;align-items:center;gap:.45rem;min-width:0;max-width:14rem;& strong{overflow:hidden;text-overflow:ellipsis;font-weight:600}}.managed-comment-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.3rem;height:1.3rem;overflow:hidden;border-radius:50%;background:rgb(from var(--muted) r g b / .15);color:var(--muted);font-size:.65rem;font-weight:600;& img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}& img[hidden]{display:none}}.managed-comment-body{grid-column:2;grid-row:1;min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;& a{color:var(--accent);text-decoration:none}& a:hover{text-decoration:underline}& a.comment-self-ref{text-decoration:underline dotted;text-underline-offset:2px}& a.comment-self-ref:hover{text-decoration:underline solid}}.managed-comment-time{color:var(--muted);font-size:.7rem;font-weight:400;white-space:nowrap;text-align:right}.managed-comment-meta{grid-column:3;grid-row:1;justify-self:end;display:flex;align-items:center;gap:.5rem;min-height:1.3rem}.managed-comment-actions{display:flex;align-items:center;gap:.15rem}.managed-comment-row:not(:has(.managed-comment-author)){grid-template-columns:minmax(0,1fr) auto;.managed-comment-body{grid-column:1}.managed-comment-meta{grid-column:2}}@container managed-comment (max-width: 28rem){.managed-comment-row:has(.managed-comment-meta){grid-template-columns:minmax(0,max-content) minmax(0,1fr);.managed-comment-meta{grid-column:1 / -1;grid-row:1}.managed-comment-author,.managed-comment-body{grid-row:2}}.managed-comment-row:not(:has(.managed-comment-author)){grid-template-columns:minmax(0,1fr)}.managed-comment-author{max-width:9rem}}
|
|
3
|
-
`;export{_ as a,v as b,y as c,b as d,C as e,w as f,T as g,E as h,D as i};
|