@preventive/triage 1.0.0-alpha.51 → 1.0.0-alpha.52
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.js +3 -6
- package/common/managed/finding-values.d.ts +2 -0
- package/common/managed/finding-values.js +44 -0
- package/common/managed/lite-name.d.ts +2 -0
- package/common/managed/lite-name.js +20 -0
- package/out/activity.js +4 -4
- package/out/{chunk-WRAJVD35.js → chunk-CHRBUGVN.js} +1 -1
- package/out/{chunk-7JBSSRD7.js → chunk-IMM274IW.js} +1 -1
- package/out/chunk-LJ7XRXFU.js +52 -0
- package/out/{chunk-PBZPYIG7.js → chunk-MYGJBYYJ.js} +1 -1
- package/out/chunk-NCN3M5EP.js +3 -0
- package/out/chunk-OGAYMEER.js +232 -0
- package/out/client-managed.js +26 -26
- package/out/compare.js +5 -5
- package/out/dialogs.js +41 -41
- package/out/index.html +3 -3
- package/out/scan.js +2 -2
- package/out/view.js +11 -11
- package/package.json +6 -1
- package/server-managed/github-requests.ts +54 -24
- package/server-managed/http.ts +192 -62
- package/server-managed/npm-kept.ts +4 -0
- package/server-managed/repository-pulse.ts +4 -2
- package/server-managed/repository-stamp.ts +41 -0
- package/server-managed/team-app.ts +3 -1
- package/server-managed/team-reports.ts +17 -10
- package/out/chunk-6LNQTKAV.js +0 -52
- package/out/chunk-IPAH4LKF.js +0 -232
- package/out/chunk-VDRXHIHN.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preventive/triage",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.52",
|
|
4
4
|
"description": "Client & relay server for triaging of automated reports",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"author": {
|
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
"server-managed/repository-pulse.ts",
|
|
199
199
|
"server-managed/repository-pulls.ts",
|
|
200
200
|
"server-managed/listed-activity.ts",
|
|
201
|
+
"server-managed/repository-stamp.ts",
|
|
201
202
|
"server-managed/repository-aliases.ts",
|
|
202
203
|
"server-managed/repository-discovery.ts",
|
|
203
204
|
"server-managed/repository-policy.ts",
|
|
@@ -285,6 +286,10 @@
|
|
|
285
286
|
"common/managed/comments.ts",
|
|
286
287
|
"common/managed/finding-detail.d.ts",
|
|
287
288
|
"common/managed/finding-detail.js",
|
|
289
|
+
"common/managed/finding-values.d.ts",
|
|
290
|
+
"common/managed/finding-values.js",
|
|
291
|
+
"common/managed/lite-name.d.ts",
|
|
292
|
+
"common/managed/lite-name.js",
|
|
288
293
|
"common/managed/github-agents.ts",
|
|
289
294
|
"common/managed/github-logins.ts",
|
|
290
295
|
"common/managed/npm-packages.d.ts",
|
|
@@ -44,15 +44,21 @@ export async function shareGithubRead<T>(fetchImpl: typeof fetch, request: strin
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// GitHub's answers to GETs that came with an ETag, by the request (its URL
|
|
47
|
-
// and credential, hashed as outstanding reads are
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
47
|
+
// and credential, hashed as outstanding reads are; an installation's token,
|
|
48
|
+
// which the App mints anew every few minutes on every instance, as the
|
|
49
|
+
// installations' one credential, GitHub telling apart which may read what):
|
|
50
|
+
// the next asked again If-None-Match, GitHub checking the credential as
|
|
51
|
+
// ever, and a 304, which it doesn't count against the rate limit, answered
|
|
52
|
+
// by the body kept. Each transport's own, as many as MAX_TAGGED_READS and
|
|
53
|
+
// MAX_TAGGED_BYTES hold, the least recently used first to go; none larger
|
|
54
|
+
// than MAX_TAGGED_READ_BYTES. Those one this process doesn't hold, read of
|
|
55
|
+
// the store kept beyond it (keepTaggedReadsIn), as a serverless instance
|
|
56
|
+
// starting anew holds none, as many as fit it (MAX_STORED_READ_BYTES).
|
|
52
57
|
const MAX_TAGGED_READS = 1000
|
|
53
58
|
const MAX_TAGGED_BYTES = 32 * 1024 * 1024
|
|
54
59
|
const MAX_TAGGED_READ_BYTES = 1024 * 1024
|
|
55
|
-
|
|
60
|
+
const MAX_STORED_READ_BYTES = 256 * 1024
|
|
61
|
+
export type TaggedRead = { etag: string; text: string }
|
|
56
62
|
const taggedReads = new WeakMap<typeof fetch, { reads: Map<string, TaggedRead>; bytes: number }>()
|
|
57
63
|
const taggedOf = (fetchImpl: typeof fetch) => {
|
|
58
64
|
let tagged = taggedReads.get(fetchImpl)
|
|
@@ -60,29 +66,50 @@ const taggedOf = (fetchImpl: typeof fetch) => {
|
|
|
60
66
|
return tagged
|
|
61
67
|
}
|
|
62
68
|
const requestKey = (request: string) => createHash('sha256').update(request).digest('hex')
|
|
69
|
+
// A GET's request as its answer is kept by: its URL and credential.
|
|
70
|
+
const taggedRequest = (url: string, token: string | null) => JSON.stringify([url, token?.startsWith('ghs_') ? { installation: true } : token])
|
|
63
71
|
|
|
64
|
-
//
|
|
65
|
-
|
|
72
|
+
// Where GitHub's tagged answers are kept beyond this process (http.ts
|
|
73
|
+
// registers the database's), by the request's key; its failures are none
|
|
74
|
+
// kept, or none written.
|
|
75
|
+
export interface TaggedStore { read(key: string): Promise<TaggedRead | null>; write(key: string, read: TaggedRead): Promise<void> }
|
|
76
|
+
let taggedStore: TaggedStore | null = null
|
|
77
|
+
export function keepTaggedReadsIn(store: TaggedStore | null): void { taggedStore = store }
|
|
78
|
+
|
|
79
|
+
// The answer kept to `request`, its ETag and body as GitHub wrote it: as
|
|
80
|
+
// this process holds it, else, where a store is kept beyond it, as that
|
|
81
|
+
// keeps it.
|
|
82
|
+
export function taggedRead(fetchImpl: typeof fetch, request: string): TaggedRead | undefined | Promise<TaggedRead | undefined> {
|
|
66
83
|
const { reads } = taggedOf(fetchImpl)
|
|
67
84
|
const key = requestKey(request)
|
|
68
85
|
const read = reads.get(key)
|
|
69
|
-
if (read) { reads.delete(key); reads.set(key, read) }
|
|
70
|
-
return
|
|
86
|
+
if (read) { reads.delete(key); reads.set(key, read); return read }
|
|
87
|
+
return taggedStore ? storedTaggedRead(fetchImpl, key, taggedStore) : undefined
|
|
88
|
+
}
|
|
89
|
+
async function storedTaggedRead(fetchImpl: typeof fetch, key: string, store: TaggedStore) {
|
|
90
|
+
const stored = await store.read(key).catch(() => null)
|
|
91
|
+
if (!stored || typeof stored.etag !== 'string' || typeof stored.text !== 'string') return undefined
|
|
92
|
+
holdTaggedRead(fetchImpl, key, stored)
|
|
93
|
+
return stored
|
|
71
94
|
}
|
|
72
95
|
|
|
73
96
|
// `text` kept as GitHub's answer to `request`, by its ETag.
|
|
74
|
-
export function keepTaggedRead(fetchImpl: typeof fetch, request: string, etag: string, text: string): void {
|
|
75
|
-
const tagged = taggedOf(fetchImpl)
|
|
97
|
+
export async function keepTaggedRead(fetchImpl: typeof fetch, request: string, etag: string, text: string): Promise<void> {
|
|
76
98
|
const key = requestKey(request)
|
|
99
|
+
holdTaggedRead(fetchImpl, key, { etag, text })
|
|
100
|
+
if (text.length <= MAX_STORED_READ_BYTES) await taggedStore?.write(key, { etag, text }).catch(() => {})
|
|
101
|
+
}
|
|
102
|
+
function holdTaggedRead(fetchImpl: typeof fetch, key: string, read: TaggedRead) {
|
|
103
|
+
const tagged = taggedOf(fetchImpl)
|
|
77
104
|
const before = tagged.reads.get(key)
|
|
78
105
|
if (before) { tagged.reads.delete(key); tagged.bytes -= before.text.length }
|
|
79
|
-
if (text.length > MAX_TAGGED_READ_BYTES) return
|
|
80
|
-
tagged.reads.set(key,
|
|
81
|
-
tagged.bytes += text.length
|
|
82
|
-
for (const [oldest,
|
|
106
|
+
if (read.text.length > MAX_TAGGED_READ_BYTES) return
|
|
107
|
+
tagged.reads.set(key, read)
|
|
108
|
+
tagged.bytes += read.text.length
|
|
109
|
+
for (const [oldest, held] of tagged.reads) {
|
|
83
110
|
if (tagged.reads.size <= MAX_TAGGED_READS && tagged.bytes <= MAX_TAGGED_BYTES) break
|
|
84
111
|
tagged.reads.delete(oldest)
|
|
85
|
-
tagged.bytes -=
|
|
112
|
+
tagged.bytes -= held.text.length
|
|
86
113
|
}
|
|
87
114
|
}
|
|
88
115
|
|
|
@@ -97,10 +124,11 @@ export function keepTaggedRead(fetchImpl: typeof fetch, request: string, etag: s
|
|
|
97
124
|
export function githubJson(url: string, token: string | null, fetchImpl: typeof fetch, method: 'GET' | 'POST' = 'GET', body?: unknown,
|
|
98
125
|
readOnly = method === 'GET'): Promise<unknown> {
|
|
99
126
|
if (readOnly) return shareGithubRead(fetchImpl, JSON.stringify([url, token, method, body]), () => githubJson(url, token, fetchImpl, method, body, false))
|
|
100
|
-
const request = method === 'GET' && body === undefined ?
|
|
101
|
-
const tagged = request === null ? undefined : taggedRead(fetchImpl, request)
|
|
127
|
+
const request = method === 'GET' && body === undefined ? taggedRequest(url, token) : null
|
|
102
128
|
// Each call told in its request's Server-Timing (request-timing.ts).
|
|
103
129
|
return timed('gh', async () => {
|
|
130
|
+
const held = request === null ? undefined : taggedRead(fetchImpl, request)
|
|
131
|
+
const tagged = held instanceof Promise ? await held : held
|
|
104
132
|
let res: Response
|
|
105
133
|
const started = Date.now()
|
|
106
134
|
try {
|
|
@@ -122,15 +150,17 @@ export function githubJson(url: string, token: string | null, fetchImpl: typeof
|
|
|
122
150
|
// The answer's body is read within the same timeout: one not read whole is
|
|
123
151
|
// GitHub not reached, as one not answered is; only one that isn't JSON is
|
|
124
152
|
// malformed.
|
|
125
|
-
|
|
153
|
+
// Kept only whole: never a 202's, GitHub still working out its answer.
|
|
154
|
+
const etag = request === null || res.status !== 200 ? null : res.headers?.get('etag') ?? null
|
|
155
|
+
let text: string | null = null, value: unknown
|
|
126
156
|
try {
|
|
127
157
|
if (etag === null) return await res.json()
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
keepTaggedRead(fetchImpl, request!, etag, text)
|
|
131
|
-
return value
|
|
158
|
+
text = await res.text()
|
|
159
|
+
value = JSON.parse(text)
|
|
132
160
|
} catch (error) {
|
|
133
161
|
throw error instanceof SyntaxError ? new GithubApiError(502, 'github-malformed') : githubUnreachable(error, started)
|
|
134
162
|
}
|
|
163
|
+
await keepTaggedRead(fetchImpl, request!, etag, text)
|
|
164
|
+
return value
|
|
135
165
|
})
|
|
136
166
|
}
|