@preventive/triage 1.0.0-alpha.5 → 1.0.0-alpha.51

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.
Files changed (368) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/README.md +16 -0
  3. package/api/reap.ts +17 -0
  4. package/cli.js +6 -0
  5. package/client/dependency-paths.d.ts +3 -0
  6. package/client/dependency-paths.js +36 -0
  7. package/client/finding-link.js +305 -0
  8. package/client/linked-findings.d.ts +1 -0
  9. package/client/linked-findings.js +115 -0
  10. package/common/bundle-commit.d.ts +1 -0
  11. package/common/bundle-commit.js +4 -0
  12. package/common/bundle-compare-diff.d.ts +3 -0
  13. package/common/bundle-compare-diff.js +357 -0
  14. package/common/bundle-compare-inputs.d.ts +2 -0
  15. package/common/bundle-compare-inputs.js +65 -0
  16. package/common/bundle-compare-versions.js +172 -0
  17. package/common/bundle-comparison.d.ts +8 -0
  18. package/common/bundle-comparison.js +62 -0
  19. package/common/bundle-metadata.d.ts +15 -0
  20. package/common/bundle-metadata.js +226 -0
  21. package/common/bundle-pkg-of.d.ts +1 -0
  22. package/common/bundle-pkg-of.js +90 -0
  23. package/common/bundle-reasons.d.ts +2 -0
  24. package/common/bundle-reasons.js +21 -0
  25. package/common/bundle-source-links.d.ts +6 -0
  26. package/common/bundle-source-links.js +216 -0
  27. package/common/bundle-sourcemap.d.ts +10 -0
  28. package/common/bundle-sourcemap.js +100 -0
  29. package/common/bundle-sources.d.ts +7 -0
  30. package/common/bundle-sources.js +333 -0
  31. package/common/bundle-stats.js +41 -0
  32. package/common/bundle-tabs.js +1 -0
  33. package/common/code-language.js +65 -0
  34. package/common/default-scan-models.ts +31 -0
  35. package/common/dependency-reviews.d.ts +18 -0
  36. package/common/dependency-reviews.js +64 -0
  37. package/common/finding-duplicates.js +58 -0
  38. package/common/finding-filters.js +274 -0
  39. package/common/finding-id.js +47 -0
  40. package/common/github-issue-labels.d.ts +1 -0
  41. package/common/github-issue-labels.js +11 -0
  42. package/common/github-pr.ts +56 -0
  43. package/common/gzip.js +59 -0
  44. package/common/line-diff.d.ts +8 -0
  45. package/common/line-diff.js +57 -0
  46. package/common/linked-workspace-groups.js +63 -0
  47. package/common/managed/agent-tasks.ts +292 -0
  48. package/common/managed/comments.ts +34 -0
  49. package/common/managed/finding-detail.d.ts +3 -0
  50. package/common/managed/finding-detail.js +66 -0
  51. package/common/managed/github-agents.ts +114 -0
  52. package/common/managed/github-logins.ts +49 -0
  53. package/common/managed/npm-packages.d.ts +7 -0
  54. package/common/managed/npm-packages.js +51 -0
  55. package/common/managed/permissions.ts +31 -0
  56. package/common/managed/report-content.ts +72 -0
  57. package/common/managed/report-filter.ts +109 -0
  58. package/common/managed/repository-alias.ts +65 -0
  59. package/common/managed/roles.ts +35 -0
  60. package/common/managed/routes.d.ts +2 -0
  61. package/common/managed/routes.js +359 -0
  62. package/common/managed/scan-models.ts +6 -0
  63. package/common/managed/team-app.d.ts +6 -0
  64. package/common/managed/team-app.js +47 -0
  65. package/common/managed/triage.ts +83 -0
  66. package/common/managed/upload-seal.ts +126 -0
  67. package/common/pretty-print.d.ts +5 -0
  68. package/common/pretty-print.js +24 -0
  69. package/common/revalidation-conflicts.js +48 -0
  70. package/common/revalidation-input-groups.js +56 -0
  71. package/common/save-error-reason.ts +20 -7
  72. package/common/scan-server.ts +13 -0
  73. package/common/security-links.d.ts +5 -0
  74. package/common/security-links.js +33 -0
  75. package/common/server-info.ts +46 -0
  76. package/common/utf8.d.ts +3 -0
  77. package/common/utf8.js +45 -0
  78. package/common/workspace-app.js +39 -0
  79. package/common/workspace-groups.js +176 -0
  80. package/out/activity.js +4 -0
  81. package/out/brotli-fallback.js +3 -3
  82. package/out/chunk-2K6UN3PC.js +3 -0
  83. package/out/chunk-2XUEZHIP.js +28 -0
  84. package/out/chunk-2ZDZWLFY.js +1 -0
  85. package/out/chunk-4HI2MSAV.js +17 -0
  86. package/out/chunk-6LNQTKAV.js +52 -0
  87. package/out/chunk-7BKKYVWR.js +7 -0
  88. package/out/chunk-7C7DWL7I.js +1 -0
  89. package/out/chunk-7JBSSRD7.js +3 -0
  90. package/out/chunk-7VAWINQF.js +6 -0
  91. package/out/chunk-C4Z6ZBXV.js +10 -0
  92. package/out/chunk-FMXWXDR4.js +36 -0
  93. package/out/chunk-GDPCLSOS.js +2 -0
  94. package/out/chunk-GO7FT4OD.js +1 -0
  95. package/out/chunk-HT2SSA3V.js +1 -0
  96. package/out/chunk-IPAH4LKF.js +232 -0
  97. package/out/chunk-IYPY47MC.js +1 -0
  98. package/out/chunk-IZ7POWI3.js +18 -0
  99. package/out/chunk-JCIIBLKR.js +1 -0
  100. package/out/chunk-KOGNQDSJ.js +1 -0
  101. package/out/chunk-NRPC74AM.js +1 -0
  102. package/out/chunk-OOJM4CTU.js +1 -0
  103. package/out/chunk-PBZPYIG7.js +1 -0
  104. package/out/chunk-PNHBZGDD.js +1 -0
  105. package/out/chunk-PWFUOLZK.js +17 -0
  106. package/out/chunk-PXK7EU4V.js +1 -0
  107. package/out/chunk-QB4CBOX2.js +1 -0
  108. package/out/chunk-R3NZFXVR.js +1 -0
  109. package/out/chunk-R5O6FAW4.js +2 -0
  110. package/out/chunk-SXV2T5C4.js +80 -0
  111. package/out/chunk-TQJDYHVE.js +2 -0
  112. package/out/chunk-TUII34K2.js +1 -0
  113. package/out/chunk-VDRXHIHN.js +3 -0
  114. package/out/chunk-VFWMQ3LG.js +2 -0
  115. package/out/chunk-VH4XMZIW.js +2 -0
  116. package/out/chunk-WCPJDAYA.js +10 -0
  117. package/out/chunk-WFYLMF6Z.js +10 -0
  118. package/out/chunk-WRAJVD35.js +1 -0
  119. package/out/chunk-WYLQWYLA.js +1 -0
  120. package/out/client-managed-import.js +4 -0
  121. package/out/client-managed.js +33 -0
  122. package/out/client-sync.js +11 -17
  123. package/out/compare.js +35 -0
  124. package/out/dialogs.js +55 -0
  125. package/out/github-issue-authorized.html +3 -0
  126. package/out/github-issue-authorized.js +1 -0
  127. package/out/graph.js +17 -4
  128. package/out/index.html +90 -8
  129. package/out/prism.js +14 -10
  130. package/out/scan.js +2 -0
  131. package/out/stasis.svg +20 -0
  132. package/out/terminal.js +366 -39
  133. package/out/view.css +1 -1
  134. package/out/view.js +19 -144
  135. package/package.json +364 -59
  136. package/risk/actions/findings.js +161 -0
  137. package/risk/actions/resolve.js +219 -0
  138. package/risk/actions/scripts/commands.js +182 -0
  139. package/risk/actions/scripts/grade.js +113 -0
  140. package/risk/actions/scripts/packages.js +123 -0
  141. package/risk/actions/scripts/shell.js +169 -0
  142. package/risk/actions/uses.js +76 -0
  143. package/risk/actions/workflow.js +110 -0
  144. package/risk/files/characters.js +196 -0
  145. package/risk/files/hosts.js +205 -0
  146. package/risk/files/readability.js +177 -0
  147. package/risk/files/runs-code.js +615 -0
  148. package/risk/files/script.js +118 -0
  149. package/risk/files/stamps.js +47 -0
  150. package/risk/index.d.ts +254 -0
  151. package/risk/index.js +11 -0
  152. package/risk/npm/assess.js +285 -0
  153. package/risk/npm/combined.js +68 -0
  154. package/risk/npm/compare.js +257 -0
  155. package/risk/npm/dependencies.js +172 -0
  156. package/risk/npm/diff.js +79 -0
  157. package/risk/npm/downloads.js +84 -0
  158. package/risk/npm/files.js +445 -0
  159. package/risk/npm/held.js +63 -0
  160. package/risk/npm/install-scripts.js +34 -0
  161. package/risk/npm/malware.js +36 -0
  162. package/risk/npm/manifest.js +65 -0
  163. package/risk/npm/new-dependency.js +60 -0
  164. package/risk/npm/old-version.js +108 -0
  165. package/risk/npm/registry.js +388 -0
  166. package/risk/npm/release.js +85 -0
  167. package/risk/npm/score.js +59 -0
  168. package/risk/npm/specs.js +124 -0
  169. package/risk/npm/tarball.js +96 -0
  170. package/server-common/database-config.ts +16 -0
  171. package/server-common/initialize.ts +18 -0
  172. package/server-common/npm-advisories.ts +101 -0
  173. package/{server → server-common}/origin.ts +5 -5
  174. package/server-common/reap.ts +48 -0
  175. package/server-common/scan-config.ts +19 -0
  176. package/server-common/standalone.ts +29 -0
  177. package/server-common/storage-crypto.ts +182 -0
  178. package/server-common/storage-log.ts +38 -0
  179. package/server-common/vercel-blob.ts +103 -0
  180. package/server-e2e/app.ts +487 -0
  181. package/{server → server-e2e}/bus-receiver.ts +2 -1
  182. package/{server → server-e2e}/cli.js +9 -4
  183. package/{server → server-e2e}/config.ts +50 -42
  184. package/{server → server-e2e}/db-neon.ts +2 -2
  185. package/{server → server-e2e}/db-revision-sql.ts +1 -1
  186. package/{server → server-e2e}/db-stmt.ts +2 -2
  187. package/{server → server-e2e}/db.ts +7 -7
  188. package/{server → server-e2e}/http.ts +44 -13
  189. package/{server → server-e2e}/hub.ts +40 -9
  190. package/server-e2e/index.ts +17 -0
  191. package/server-e2e/lifecycle.ts +95 -0
  192. package/{server → server-e2e}/neon-driver.ts +2 -2
  193. package/{server → server-e2e}/npm-proxy.ts +12 -145
  194. package/{server → server-e2e}/objstore/blob-vercel.ts +10 -114
  195. package/{server → server-e2e}/objstore/blob.ts +1 -1
  196. package/server-e2e/objstore/fetch-mint-guard.ts +74 -0
  197. package/{server → server-e2e}/objstore/handlers.ts +9 -8
  198. package/{server → server-e2e}/objstore/init.ts +19 -27
  199. package/{server → server-e2e}/objstore/reaper.ts +1 -1
  200. package/server-e2e/objstore/rest-deny.ts +28 -0
  201. package/server-e2e/objstore/rest-mint.ts +224 -0
  202. package/{server → server-e2e}/objstore/rest.ts +3 -3
  203. package/{server → server-e2e}/objstore/store-neon.ts +6 -10
  204. package/{server → server-e2e}/objstore/store.ts +7 -7
  205. package/{server → server-e2e}/objstore/tokens.ts +2 -2
  206. package/{server → server-e2e}/pubsub.ts +12 -9
  207. package/{server → server-e2e}/sign.ts +4 -4
  208. package/{server → server-e2e}/sse-server.ts +60 -33
  209. package/{server → server-e2e}/static.ts +31 -21
  210. package/{server → server-e2e}/sync-handlers.ts +64 -30
  211. package/{server → server-e2e}/ws-server.ts +20 -9
  212. package/server-managed/activity.ts +258 -0
  213. package/server-managed/agent-merges.ts +109 -0
  214. package/server-managed/app-agents.ts +23 -0
  215. package/server-managed/avatar-store.ts +13 -0
  216. package/server-managed/blob-store.ts +17 -0
  217. package/server-managed/brotli.ts +10 -0
  218. package/server-managed/bundle-advisories.ts +120 -0
  219. package/server-managed/bundle-advisory-inventory.ts +77 -0
  220. package/server-managed/bundle-build-diagnostics.d.ts +10 -0
  221. package/server-managed/bundle-build-diagnostics.js +23 -0
  222. package/server-managed/bundle-build-leases.ts +41 -0
  223. package/server-managed/bundle-build-worker.js +115 -0
  224. package/server-managed/bundle-build.ts +200 -0
  225. package/server-managed/bundle-cache.ts +211 -0
  226. package/server-managed/bundle-catalog.ts +29 -0
  227. package/server-managed/bundle-commits.ts +140 -0
  228. package/server-managed/bundle-store.ts +24 -0
  229. package/server-managed/bundle-summary-cache.ts +130 -0
  230. package/server-managed/bundle.ts +119 -0
  231. package/server-managed/cache-storage.ts +20 -0
  232. package/server-managed/cli.js +21 -0
  233. package/server-managed/combined.ts +47 -0
  234. package/server-managed/comments.ts +151 -0
  235. package/server-managed/commit-coauthors.ts +106 -0
  236. package/server-managed/commit-lockfiles.ts +76 -0
  237. package/server-managed/comparison.ts +52 -0
  238. package/server-managed/config.ts +164 -0
  239. package/server-managed/content-access.ts +15 -0
  240. package/server-managed/crypto.ts +25 -0
  241. package/server-managed/db-methods.ts +1923 -0
  242. package/server-managed/db-neon.ts +468 -0
  243. package/server-managed/db-schema.ts +238 -0
  244. package/server-managed/db-table-names.ts +22 -0
  245. package/server-managed/db.ts +143 -0
  246. package/server-managed/dependabot-access.ts +60 -0
  247. package/server-managed/dependabot-alerts.ts +185 -0
  248. package/server-managed/dependency-review-comments.ts +98 -0
  249. package/server-managed/dependency-reviews.ts +364 -0
  250. package/server-managed/github-accounts.ts +60 -0
  251. package/server-managed/github-actions.ts +71 -0
  252. package/server-managed/github-app.ts +376 -0
  253. package/server-managed/github-errors.ts +70 -0
  254. package/server-managed/github-graphql.ts +39 -0
  255. package/server-managed/github-issue-links.ts +140 -0
  256. package/server-managed/github-issue-oauth.ts +47 -0
  257. package/server-managed/github-issues.ts +152 -0
  258. package/server-managed/github-link-oauth.ts +69 -0
  259. package/server-managed/github-links.ts +204 -0
  260. package/server-managed/github-metadata.ts +213 -0
  261. package/server-managed/github-oauth.ts +238 -0
  262. package/server-managed/github-pulls.ts +227 -0
  263. package/server-managed/github-requests.ts +136 -0
  264. package/server-managed/github-users.ts +30 -0
  265. package/server-managed/http-response.ts +76 -0
  266. package/server-managed/http.ts +5503 -0
  267. package/server-managed/import-triage.ts +51 -0
  268. package/server-managed/index.ts +192 -0
  269. package/server-managed/kept-briefly.ts +37 -0
  270. package/server-managed/kept.ts +28 -0
  271. package/server-managed/link-reports.ts +162 -0
  272. package/server-managed/listed-activity.ts +39 -0
  273. package/server-managed/locked-packages.ts +152 -0
  274. package/server-managed/lockfile-scope.ts +49 -0
  275. package/server-managed/lru.ts +30 -0
  276. package/server-managed/managed-issues.ts +78 -0
  277. package/server-managed/management-catalog.ts +45 -0
  278. package/server-managed/management.ts +99 -0
  279. package/server-managed/npm-delta-worker.js +23 -0
  280. package/server-managed/npm-delta.ts +194 -0
  281. package/server-managed/npm-insights.ts +449 -0
  282. package/server-managed/npm-kept.ts +225 -0
  283. package/server-managed/npm-loads.ts +264 -0
  284. package/server-managed/npm-manifest.ts +115 -0
  285. package/server-managed/npm-packages.ts +454 -0
  286. package/server-managed/npm-patch.ts +325 -0
  287. package/server-managed/npm-provenance.ts +123 -0
  288. package/server-managed/npm-risk.ts +285 -0
  289. package/server-managed/object-storage-disk.ts +148 -0
  290. package/server-managed/object-storage-vercel.ts +67 -0
  291. package/server-managed/object-storage.ts +75 -0
  292. package/server-managed/package-entry-points.ts +51 -0
  293. package/server-managed/pretty-print.ts +434 -0
  294. package/server-managed/public-workspace.ts +238 -0
  295. package/server-managed/pull-comment-reviews.ts +161 -0
  296. package/server-managed/pull-dependencies.ts +351 -0
  297. package/server-managed/pull-dependency-reviews.ts +334 -0
  298. package/server-managed/pull-reviews.ts +205 -0
  299. package/server-managed/queue.ts +39 -0
  300. package/server-managed/release-commits.ts +84 -0
  301. package/server-managed/rename-diff-worker.js +26 -0
  302. package/server-managed/rename-diff.ts +279 -0
  303. package/server-managed/repo-path.ts +21 -0
  304. package/server-managed/report-migration.ts +35 -0
  305. package/server-managed/report-query.ts +10 -0
  306. package/server-managed/report-response.ts +16 -0
  307. package/server-managed/report-sources.ts +178 -0
  308. package/server-managed/report-store.ts +85 -0
  309. package/server-managed/repository-actions.ts +218 -0
  310. package/server-managed/repository-agents.ts +448 -0
  311. package/server-managed/repository-aliases.ts +91 -0
  312. package/server-managed/repository-browser.ts +294 -0
  313. package/server-managed/repository-changes.ts +284 -0
  314. package/server-managed/repository-code.ts +215 -0
  315. package/server-managed/repository-dependency-reviews.ts +166 -0
  316. package/server-managed/repository-discovery.ts +82 -0
  317. package/server-managed/repository-history.ts +432 -0
  318. package/server-managed/repository-issues.ts +195 -0
  319. package/server-managed/repository-policy.ts +27 -0
  320. package/server-managed/repository-pulls.ts +212 -0
  321. package/server-managed/repository-pulse.ts +460 -0
  322. package/server-managed/repository-risk-assess.ts +105 -0
  323. package/server-managed/repository-risk-audit.ts +79 -0
  324. package/server-managed/repository-risk-read.ts +170 -0
  325. package/server-managed/repository-risk.ts +491 -0
  326. package/server-managed/repository-squashes.ts +168 -0
  327. package/server-managed/request-timing.ts +63 -0
  328. package/server-managed/reviewed-dependencies.ts +149 -0
  329. package/server-managed/revisions.ts +35 -0
  330. package/server-managed/scan-downloads.ts +87 -0
  331. package/server-managed/session.ts +132 -0
  332. package/server-managed/slugs.ts +38 -0
  333. package/server-managed/solidity-entry-points.ts +71 -0
  334. package/server-managed/sql-postgres.ts +34 -0
  335. package/server-managed/sql.ts +88 -0
  336. package/server-managed/static.ts +28 -0
  337. package/server-managed/storage-command.ts +16 -0
  338. package/server-managed/storage-db.ts +183 -0
  339. package/server-managed/storage-encryption.ts +290 -0
  340. package/server-managed/storage-maintenance.ts +259 -0
  341. package/server-managed/storage-payload.ts +121 -0
  342. package/server-managed/storage-stores.ts +91 -0
  343. package/server-managed/storage.ts +82 -0
  344. package/server-managed/team-app.ts +134 -0
  345. package/server-managed/team-catalog.ts +7 -0
  346. package/server-managed/team-feed.ts +180 -0
  347. package/server-managed/team-npm-scopes.ts +106 -0
  348. package/server-managed/team-reports.ts +319 -0
  349. package/server-managed/triage-response.ts +16 -0
  350. package/server-managed/type-only-worker.js +19 -0
  351. package/server-managed/type-only.ts +105 -0
  352. package/server-managed/uploads.ts +106 -0
  353. package/server-managed/upstream-cache.ts +243 -0
  354. package/server-managed/view-as.ts +58 -0
  355. package/server-managed/workspace-shares.ts +169 -0
  356. package/server.ts +70 -0
  357. package/server/index.ts +0 -480
  358. package/server/lifecycle.ts +0 -208
  359. /package/{LICENSE → LICENSE-MIT} +0 -0
  360. /package/{server → server-e2e}/auth.ts +0 -0
  361. /package/{server → server-e2e}/config.example.json +0 -0
  362. /package/{server → server-e2e}/objstore/blob-fs.ts +0 -0
  363. /package/{server → server-e2e}/objstore/fs.ts +0 -0
  364. /package/{server → server-e2e}/objstore/sign.ts +0 -0
  365. /package/{server → server-e2e}/peer.ts +0 -0
  366. /package/{server → server-e2e}/sse-session.ts +0 -0
  367. /package/{server → server-e2e}/util.ts +0 -0
  368. /package/{server → server-e2e}/validation.ts +0 -0
@@ -1,208 +0,0 @@
1
- // Process lifecycle: the in-flight request tracker, the `shuttingDown`
2
- // gate, the graceful-shutdown choreography, and the signal / error /
3
- // uncaught-exception handlers that drive it. Two-phase so the bits the
4
- // HTTP + WS planes need (`track`, `isShuttingDown`) exist BEFORE those
5
- // servers are built, while the teardown is `install()`ed afterward —
6
- // once the server objects, heartbeat timer, and reaper exist.
7
-
8
- import type { Server } from 'node:http'
9
- import type { WebSocketServer } from 'ws'
10
- import type { SseSession } from './sse-session.ts'
11
- import { errStack } from './util.ts'
12
-
13
- export type ShutdownDeps = {
14
- httpServer: Server
15
- wss: WebSocketServer
16
- heartbeatTimer: ReturnType<typeof setInterval>
17
- // The SSE keepalive-sweep timer (server/sse-server.ts). Cleared on
18
- // shutdown alongside `heartbeatTimer` so a tick can't write a keepalive
19
- // comment to a session the close loop below is already tearing down.
20
- sseKeepaliveTimer: ReturnType<typeof setInterval>
21
- // Stops the periodic reaper AND awaits any in-flight sweep.
22
- stopReaper: () => Promise<void>
23
- // Live SSE+POST session iterator (mirrors `wss.clients` for the SSE
24
- // fallback transport). Walked alongside `wss.clients` to send the
25
- // 1001 close frame and apply the terminate-grace timer to both
26
- // transports uniformly.
27
- sseSessions: () => Iterable<SseSession>
28
- // App-specific teardown, run AFTER the in-flight drain: close the DB.
29
- // Swallows its own errors (a failed close shouldn't abort the exit).
30
- closeDb: () => Promise<void>
31
- }
32
-
33
- export type Lifecycle = {
34
- // Collects in-flight async handlers so `shutdown` can drain them
35
- // before closing the DB. Passed to the HTTP + WS planes.
36
- track: (promise: Promise<unknown>) => void
37
- // Read by the REST shutdown gate + the WS message loop to drop new
38
- // work once teardown began.
39
- isShuttingDown: () => boolean
40
- // Wire the graceful shutdown + the error / signal / process-catchall
41
- // handlers. Call once, after the server objects exist.
42
- install: (deps: ShutdownDeps) => void
43
- }
44
-
45
- export function createLifecycle(): Lifecycle {
46
- // In-flight async message handlers. `shutdown` awaits this set before
47
- // closing the DB so a SIGINT mid-save can't resume against a closed
48
- // handle (which would throw inside the commit's gated INSERT after
49
- // the client believed its save was committed).
50
- const inFlight = new Set<Promise<unknown>>()
51
- function track(promise: Promise<unknown>): void {
52
- inFlight.add(promise)
53
- // Trailing `.catch(() => {})` swallows the rejection that `.finally`
54
- // propagates through its returned promise — without it, a tracked
55
- // handler that rejects (or whose caller's catch handler itself
56
- // throws) trips the `unhandledRejection` catchall below and crashes
57
- // the process via `fireShutdown(1)`.
58
- promise.finally(() => inFlight.delete(promise)).catch(() => {})
59
- }
60
- let shuttingDown = false
61
- // Live exit code the in-progress shutdown will pass to `process.exit`.
62
- // Re-entry can ESCALATE it from 0 → 1 (e.g. a `wss.error` firing
63
- // during a SIGTERM-driven graceful shutdown shouldn't leave the
64
- // launcher seeing a clean exit code) but can never DE-escalate.
65
- // Audit round-13.
66
- let pendingExitCode = 0
67
-
68
- function install(deps: ShutdownDeps): void {
69
- const { httpServer, wss, heartbeatTimer, sseKeepaliveTimer, stopReaper, sseSessions, closeDb } = deps
70
-
71
- async function shutdown(exitCode: number = 0): Promise<void> {
72
- // Re-entry: don't restart the teardown, but escalate the pending
73
- // exit code if the new caller is non-zero (e.g. a wss.error during
74
- // a SIGTERM-driven graceful shutdown). Without this, an error
75
- // arriving mid-shutdown would silently exit 0 and the launcher
76
- // would record a clean stop.
77
- if (shuttingDown) {
78
- if (exitCode !== 0 && pendingExitCode === 0) pendingExitCode = exitCode
79
- return
80
- }
81
- shuttingDown = true
82
- pendingExitCode = exitCode
83
- console.log('Shutting down…')
84
- // Stop both periodic timers (WS heartbeat + SSE keepalive) so neither
85
- // fires mid-shutdown against a peer the close-loop is tearing down.
86
- for (const timer of [heartbeatTimer, sseKeepaliveTimer]) clearInterval(timer)
87
- // Send a 1001 (going away) close frame to every open socket BEFORE
88
- // shutting the listener. Lets clients distinguish a server-initiated
89
- // graceful shutdown from a network drop, so they can skip their
90
- // reconnect backoff. Fire-and-forget — `process.exit` below would
91
- // force-kill any in-progress flush anyway. `try/catch` shrugs at
92
- // sockets already in CLOSING / CLOSED.
93
- for (const socket of wss.clients) {
94
- try { socket.close(1001, 'Server shutting down') } catch {}
95
- }
96
- // Same close frame on every SSE+POST session — the adapter
97
- // translates `close(1001, …)` into a structured `event: close`
98
- // record on the SSE stream so the client transport can read the
99
- // code and skip its reconnect backoff (parity with the WS path).
100
- for (const session of sseSessions()) {
101
- try { session.close(1001, 'Server shutting down') } catch {}
102
- }
103
- // Force-terminate any client that doesn't ack the close frame
104
- // within a short grace window. `wss.close()` waits for every client
105
- // to emit `'close'`, and `ws` only TCP-RSTs unresponsive peers
106
- // after its own ~30 s `closeTimeout`. A single dead/blackholed peer
107
- // would otherwise stretch SIGTERM/SIGINT response by that full
108
- // timeout. Audit round-11.
109
- const TERMINATE_GRACE_MS = 1_000
110
- const terminateTimer = setTimeout(() => {
111
- for (const socket of wss.clients) {
112
- const rs = socket.readyState
113
- if (rs === socket.OPEN || rs === socket.CLOSING) {
114
- try { socket.terminate() } catch {}
115
- }
116
- }
117
- // SSE sessions are tracked separately from `wss.clients`; apply
118
- // the same terminate-grace policy so a stranded SSE response
119
- // can't pin the HTTP keep-alive shutdown branch below.
120
- for (const session of sseSessions()) {
121
- const rs = session.readyState
122
- if (rs === session.OPEN || rs === session.CLOSING) {
123
- try { session.terminate() } catch {}
124
- }
125
- }
126
- // Same grace for HTTP keep-alive sockets that didn't respect the
127
- // `Connection: close` hint, which would otherwise hold
128
- // `httpServer.close()` until their TCP timeout.
129
- try { httpServer.closeAllConnections() } catch {}
130
- }, TERMINATE_GRACE_MS)
131
- // Don't keep the event loop alive solely for the grace timer.
132
- terminateTimer.unref?.()
133
- // Stop the periodic reaper AND wait for any in-flight sweep (incl.
134
- // the startup sweep) before the DB close — otherwise a
135
- // readdir / unlink would race a closed DB.
136
- await stopReaper()
137
- // Free idle HTTP keep-alive sockets up front so close() below
138
- // doesn't wait on them. Active in-flight requests still finish.
139
- try { httpServer.closeIdleConnections() } catch {}
140
- // Close http.Server first to stop accepting new upgrades + HTTP
141
- // requests. Guard with `.listening` because `close()` throws
142
- // ERR_SERVER_NOT_RUNNING when bind never succeeded (the http error
143
- // handler is the path that invoked shutdown in that case).
144
- if (httpServer.listening) {
145
- await new Promise<void>((resolve) => { httpServer.close(() => resolve()) })
146
- }
147
- await new Promise<void>((resolve) => { wss.close(() => resolve()) })
148
- clearTimeout(terminateTimer)
149
- // Drain in-flight handlers so a save that's mid-pipeline finishes
150
- // its commit before the DB closes. `handleSave` splits its
151
- // canonical/id/dup-precheck/verify/commit work across awaits, so
152
- // the window spans several yield points. `Promise.allSettled` so a
153
- // single handler rejection doesn't abort the drain.
154
- if (inFlight.size > 0) await Promise.allSettled([...inFlight])
155
- // App teardown AFTER the drain: close the DB.
156
- await closeDb()
157
- // Read `pendingExitCode` (not the parameter) so a re-entrant
158
- // `shutdown(1)` that landed during the drain wins over the original
159
- // `shutdown(0)`. See round-13 escalation note.
160
- process.exit(pendingExitCode)
161
- }
162
-
163
- // `.catch` defends against an unguarded `await` slipping into
164
- // `shutdown`: an unhandled rejection there would skip the non-zero
165
- // exit the launcher relies on.
166
- function fireShutdown(code: number): void {
167
- shutdown(code).catch((err) => {
168
- console.warn('shutdown error:', errStack(err))
169
- process.exit(code === 0 ? 1 : code)
170
- })
171
- }
172
-
173
- // Route bind / post-listen failures through `shutdown` so the
174
- // in-flight drain + DB close still run before exit. Without this,
175
- // `ws` re-emits the error as uncaughtException and the launcher sees
176
- // a confusing crash rather than the bind failure. Audit round-9 M2.
177
- httpServer.on('error', (err: Error) => {
178
- console.error('Server error:', errStack(err))
179
- fireShutdown(1)
180
- })
181
- // Symmetric with the http.Server error handler — route through
182
- // `fireShutdown(1)` so the launcher sees a non-zero exit, and the
183
- // re-entry escalation bumps `pendingExitCode` 0 → 1 for a `wss.error`
184
- // arriving mid-graceful-SIGTERM.
185
- wss.on('error', (err: Error) => {
186
- console.error('WS server error:', errStack(err))
187
- fireShutdown(1)
188
- })
189
- // Wrap signal handlers so the signal name (the listener's first arg)
190
- // doesn't bleed into shutdown's `exitCode`.
191
- process.on('SIGINT', () => fireShutdown(0))
192
- process.on('SIGTERM', () => fireShutdown(0))
193
- // Process-level catchalls so a stray rejection / uncaught exception
194
- // doesn't bypass `shutdown()` — Node 20+ exits on unhandled
195
- // rejection, which would skip the drain + DB close. Log forensically
196
- // and route through `fireShutdown(1)`. Audit round-11 observability.
197
- process.on('unhandledRejection', (reason) => {
198
- console.error('Unhandled rejection:', errStack(reason))
199
- fireShutdown(1)
200
- })
201
- process.on('uncaughtException', (err) => {
202
- console.error('Uncaught exception:', errStack(err))
203
- fireShutdown(1)
204
- })
205
- }
206
-
207
- return { track, isShuttingDown: () => shuttingDown, install }
208
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes