@preventive/triage 1.0.0-alpha.18 → 1.0.0-alpha.19
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/api/reap.ts +13 -75
- package/common/server-info.ts +2 -0
- package/out/client-managed-import.js +81 -0
- package/out/client-managed.js +67 -56
- package/out/client-sync.js +1 -1
- package/out/graph.js +10 -10
- package/out/view.css +1 -1
- package/out/view.js +169 -162
- package/package.json +12 -2
- package/report/index.js +2 -1
- package/report/src/finding.js +12 -0
- package/report/src/security.js +63 -0
- package/server-common/database-config.ts +16 -0
- package/server-common/initialize.ts +18 -0
- package/server-common/npm-advisories.ts +101 -0
- package/server-common/reap.ts +48 -0
- package/server-common/standalone.ts +29 -0
- package/server-common/storage-log.ts +34 -0
- package/server-common/vercel-blob.ts +110 -0
- package/server-e2e/app.ts +485 -0
- package/server-e2e/cli.js +7 -2
- package/server-e2e/config.ts +36 -40
- package/server-e2e/db-neon.ts +1 -1
- package/server-e2e/db.ts +1 -1
- package/server-e2e/http.ts +12 -2
- package/server-e2e/index.ts +12 -488
- package/server-e2e/lifecycle.ts +59 -172
- package/server-e2e/neon-driver.ts +2 -2
- package/server-e2e/npm-proxy.ts +10 -143
- package/server-e2e/objstore/blob-vercel.ts +4 -108
- package/server-e2e/objstore/init.ts +15 -23
- package/server-e2e/objstore/store-neon.ts +5 -9
|
@@ -18,9 +18,9 @@ export type ObjstoreInitDeps = {
|
|
|
18
18
|
// the workspace_revision handle in server-e2e/db.ts.
|
|
19
19
|
handle: Handle
|
|
20
20
|
reapIntervalMs: number
|
|
21
|
-
//
|
|
21
|
+
// Automatic-sweep switch (OBJSTORE_REAP_DISABLED). When true, NEITHER the boot
|
|
22
22
|
// sweep nor the periodic timer runs: `startupReap` resolves immediately
|
|
23
|
-
// and
|
|
23
|
+
// and no timer is installed. Orphaned/superseded blobs and stale
|
|
24
24
|
// staging rows then accumulate unbounded — only safe if an external job
|
|
25
25
|
// handles GC. Omitted/false → reaper runs (the default). See config.ts.
|
|
26
26
|
reapDisabled?: boolean
|
|
@@ -59,6 +59,7 @@ export type ObjstoreInitDeps = {
|
|
|
59
59
|
export type ObjstoreInit = {
|
|
60
60
|
handlers: ObjstoreHandlers
|
|
61
61
|
restDeps: ObjstoreRestDeps
|
|
62
|
+
reap: () => Promise<void>
|
|
62
63
|
startupReap: Promise<void>
|
|
63
64
|
// Cancels the periodic timer AND resolves only after any
|
|
64
65
|
// currently-in-flight sweep has finished, so shutdown can safely
|
|
@@ -102,31 +103,22 @@ export function initObjstore(deps: ObjstoreInitDeps): ObjstoreInit {
|
|
|
102
103
|
let inFlight: Promise<void> | null = null
|
|
103
104
|
function enqueueSweep(): Promise<void> {
|
|
104
105
|
if (inFlight) return inFlight
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
// cleaned, and operators need to see it. Inner catch makes the
|
|
108
|
-
// promise resolve so callers (startupReap awaiter + setInterval)
|
|
109
|
-
// don't have to handle rejection.
|
|
110
|
-
const p = reapOrphans(handle).catch((err) => {
|
|
111
|
-
console.warn('objstore reaper error:', errStack(err))
|
|
112
|
-
}).finally(() => { if (inFlight === p) inFlight = null })
|
|
106
|
+
// Keep failures visible to explicit callers; automatic sweeps log them.
|
|
107
|
+
const p = reapOrphans(handle).finally(() => { if (inFlight === p) inFlight = null })
|
|
113
108
|
inFlight = p
|
|
114
109
|
return p
|
|
115
110
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
// nothing to clear or drain. Loud, unconditional warning — with GC off,
|
|
120
|
-
// orphaned/superseded blobs and stale staging rows are never reclaimed
|
|
121
|
-
// (they accumulate until an external job, if any, collects them).
|
|
111
|
+
const drain = async () => { if (inFlight) await inFlight.catch(() => {}) }
|
|
112
|
+
const automaticSweep = () => enqueueSweep().catch(err => console.warn('objstore reaper error:', errStack(err)))
|
|
113
|
+
// Explicit cleanup remains available when automatic sweeps are disabled.
|
|
122
114
|
if (deps.reapDisabled) {
|
|
123
|
-
console.warn('objstore: reaper DISABLED (OBJSTORE_REAP_DISABLED) —
|
|
124
|
-
return { handlers, restDeps, startupReap: Promise.resolve(), stopReaper:
|
|
115
|
+
console.warn('objstore: automatic reaper DISABLED (OBJSTORE_REAP_DISABLED) — use /api/reap or an external cleanup job')
|
|
116
|
+
return { handlers, restDeps, reap: enqueueSweep, startupReap: Promise.resolve(), stopReaper: drain }
|
|
125
117
|
}
|
|
126
118
|
// Caller awaits this before accepting traffic so a fresh boot
|
|
127
119
|
// can't hand out list / fetch / put-begin against a tag whose
|
|
128
120
|
// on-disk state still has stranded files from a prior crash.
|
|
129
|
-
const startupReap =
|
|
121
|
+
const startupReap = automaticSweep()
|
|
130
122
|
// Jittered start of the periodic timer. Multi-replica deploys
|
|
131
123
|
// (Neon + Vercel Blob) commonly boot N replicas in tight lock-step
|
|
132
124
|
// (deploy rollout, cluster restart), which would otherwise sync every
|
|
@@ -139,20 +131,20 @@ export function initObjstore(deps: ObjstoreInitDeps): ObjstoreInit {
|
|
|
139
131
|
let reapTimer: ReturnType<typeof setInterval> | null = null
|
|
140
132
|
const jitterMs = Math.floor(Math.random() * deps.reapIntervalMs)
|
|
141
133
|
const firstTimer = setTimeout(() => {
|
|
142
|
-
|
|
143
|
-
reapTimer = setInterval(
|
|
134
|
+
void automaticSweep()
|
|
135
|
+
reapTimer = setInterval(automaticSweep, deps.reapIntervalMs)
|
|
144
136
|
reapTimer.unref?.()
|
|
145
137
|
}, deps.reapIntervalMs + jitterMs)
|
|
146
138
|
firstTimer.unref?.()
|
|
147
139
|
return {
|
|
148
|
-
handlers, restDeps, startupReap,
|
|
140
|
+
handlers, restDeps, reap: enqueueSweep, startupReap,
|
|
149
141
|
stopReaper: async () => {
|
|
150
142
|
clearTimeout(firstTimer)
|
|
151
143
|
if (reapTimer) clearInterval(reapTimer)
|
|
152
144
|
// Drain whichever sweep is currently running (startup or
|
|
153
145
|
// periodic) — either could be mid-readdir/unlink at SIGTERM
|
|
154
146
|
// time and would otherwise outlive `handle.close()`.
|
|
155
|
-
|
|
147
|
+
await drain()
|
|
156
148
|
},
|
|
157
149
|
}
|
|
158
150
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// dialect on the wire.
|
|
5
5
|
//
|
|
6
6
|
// `@neondatabase/serverless` is an OPTIONAL peer dep — selected by
|
|
7
|
-
// the
|
|
7
|
+
// the configured database URL branch in `server-e2e/index.ts`. The peer dep
|
|
8
8
|
// itself is loaded lazily inside `openNeonObjstore` below. A single
|
|
9
9
|
// Neon project / database holds both `workspace_revision` and the
|
|
10
10
|
// two objstore tables; each plane opens its own stateless
|
|
@@ -13,14 +13,10 @@
|
|
|
13
13
|
// release.
|
|
14
14
|
//
|
|
15
15
|
// Byte plane: passed in as a `BlobBackend` (./blob.ts). The
|
|
16
|
-
// supported
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// alongside DATABASE_URL.
|
|
21
|
-
// - Neon + local FS (development / operator escape hatch — bytes
|
|
22
|
-
// still need to live on a shared filesystem if you run more
|
|
23
|
-
// than one replica).
|
|
16
|
+
// supported Neon pairing selected by server-e2e/index.ts is Vercel
|
|
17
|
+
// Blob Private Storage. DATABASE_URL or E2E_DATABASE_URL plus BLOB_READ_WRITE_TOKEN
|
|
18
|
+
// is required; the launcher never selects Neon + local FS. Tests
|
|
19
|
+
// can inject a local-FS backend directly through this adapter.
|
|
24
20
|
|
|
25
21
|
import type { BlobBackend } from './blob.ts'
|
|
26
22
|
import type { AllStmt, GetStmt, RunStmt } from '../db-stmt.ts'
|