cursedbelt-server 1.0.0

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 (606) hide show
  1. package/LICENSE +15 -0
  2. package/dist/server/activity/index.d.ts +36 -0
  3. package/dist/server/activity/index.js +36 -0
  4. package/dist/server/activity/migrations.d.ts +21 -0
  5. package/dist/server/activity/migrations.js +55 -0
  6. package/dist/server/activity/shareSource.d.ts +11 -0
  7. package/dist/server/activity/shareSource.js +45 -0
  8. package/dist/server/activity/store.d.ts +123 -0
  9. package/dist/server/activity/store.js +178 -0
  10. package/dist/server/app.d.ts +56 -0
  11. package/dist/server/app.js +45 -0
  12. package/dist/server/audit/index.d.ts +2 -0
  13. package/dist/server/audit/index.js +2 -0
  14. package/dist/server/audit/migrations.d.ts +2 -0
  15. package/dist/server/audit/migrations.js +79 -0
  16. package/dist/server/audit/sqliteSink.d.ts +36 -0
  17. package/dist/server/audit/sqliteSink.js +95 -0
  18. package/dist/server/audit/sqliteSink.test.d.ts +1 -0
  19. package/dist/server/audit/sqliteSink.test.js +180 -0
  20. package/dist/server/auth/cookie.d.ts +22 -0
  21. package/dist/server/auth/cookie.js +35 -0
  22. package/dist/server/auth/csrf.d.ts +42 -0
  23. package/dist/server/auth/csrf.js +36 -0
  24. package/dist/server/auth/fileTokenSigner.d.ts +10 -0
  25. package/dist/server/auth/fileTokenSigner.js +22 -0
  26. package/dist/server/auth/getOnly.d.ts +20 -0
  27. package/dist/server/auth/getOnly.js +27 -0
  28. package/dist/server/auth/index.d.ts +12 -0
  29. package/dist/server/auth/index.js +15 -0
  30. package/dist/server/auth/jwt.d.ts +18 -0
  31. package/dist/server/auth/jwt.js +56 -0
  32. package/dist/server/auth/loginThrottle.d.ts +118 -0
  33. package/dist/server/auth/loginThrottle.js +204 -0
  34. package/dist/server/auth/middleware.d.ts +14 -0
  35. package/dist/server/auth/middleware.js +47 -0
  36. package/dist/server/auth/password.d.ts +14 -0
  37. package/dist/server/auth/password.js +65 -0
  38. package/dist/server/auth/sessionStore.d.ts +19 -0
  39. package/dist/server/auth/sessionStore.js +221 -0
  40. package/dist/server/auth/totp.d.ts +34 -0
  41. package/dist/server/auth/totp.js +81 -0
  42. package/dist/server/auth/types.d.ts +133 -0
  43. package/dist/server/auth/types.js +1 -0
  44. package/dist/server/auth/vault.d.ts +27 -0
  45. package/dist/server/auth/vault.js +57 -0
  46. package/dist/server/context.d.ts +35 -0
  47. package/dist/server/context.js +1 -0
  48. package/dist/server/ctgr.d.ts +32 -0
  49. package/dist/server/ctgr.js +69 -0
  50. package/dist/server/db/kysely.d.ts +14 -0
  51. package/dist/server/db/kysely.js +16 -0
  52. package/dist/server/db/plumbingTypes.d.ts +116 -0
  53. package/dist/server/db/plumbingTypes.js +12 -0
  54. package/dist/server/dropzone/duplicates.d.ts +82 -0
  55. package/dist/server/dropzone/duplicates.js +90 -0
  56. package/dist/server/dropzone/index.d.ts +13 -0
  57. package/dist/server/dropzone/index.js +13 -0
  58. package/dist/server/dropzone/scan.d.ts +166 -0
  59. package/dist/server/dropzone/scan.js +272 -0
  60. package/dist/server/errors.d.ts +7 -0
  61. package/dist/server/errors.js +7 -0
  62. package/dist/server/guard/argon2Binding.d.ts +97 -0
  63. package/dist/server/guard/argon2Binding.js +138 -0
  64. package/dist/server/guard/index.d.ts +4 -0
  65. package/dist/server/guard/index.js +6 -0
  66. package/dist/server/guard/kdfWorker.d.ts +101 -0
  67. package/dist/server/guard/kdfWorker.js +301 -0
  68. package/dist/server/guard/kdfWorkerEntry.d.ts +1 -0
  69. package/dist/server/guard/kdfWorkerEntry.js +15 -0
  70. package/dist/server/guard/passwordVerifier.d.ts +65 -0
  71. package/dist/server/guard/passwordVerifier.js +122 -0
  72. package/dist/server/guard/revocationStore.d.ts +132 -0
  73. package/dist/server/guard/revocationStore.js +211 -0
  74. package/dist/server/guard/sessionSigner.d.ts +132 -0
  75. package/dist/server/guard/sessionSigner.js +303 -0
  76. package/dist/server/guard/verifier.d.ts +21 -0
  77. package/dist/server/guard/verifier.js +1 -0
  78. package/dist/server/incidents/channels.d.ts +161 -0
  79. package/dist/server/incidents/channels.js +357 -0
  80. package/dist/server/incidents/checks.d.ts +61 -0
  81. package/dist/server/incidents/checks.js +55 -0
  82. package/dist/server/incidents/index.d.ts +51 -0
  83. package/dist/server/incidents/index.js +51 -0
  84. package/dist/server/incidents/migrations.d.ts +20 -0
  85. package/dist/server/incidents/migrations.js +58 -0
  86. package/dist/server/incidents/notifier.d.ts +79 -0
  87. package/dist/server/incidents/notifier.js +141 -0
  88. package/dist/server/incidents/reporter.d.ts +60 -0
  89. package/dist/server/incidents/reporter.js +49 -0
  90. package/dist/server/incidents/routes.d.ts +91 -0
  91. package/dist/server/incidents/routes.js +202 -0
  92. package/dist/server/incidents/store.d.ts +75 -0
  93. package/dist/server/incidents/store.js +176 -0
  94. package/dist/server/incidents/types.d.ts +107 -0
  95. package/dist/server/incidents/types.js +32 -0
  96. package/dist/server/index.d.ts +25 -0
  97. package/dist/server/index.js +53 -0
  98. package/dist/server/jobs/fireQueue.d.ts +12 -0
  99. package/dist/server/jobs/fireQueue.js +37 -0
  100. package/dist/server/jobs/index.d.ts +6 -0
  101. package/dist/server/jobs/index.js +10 -0
  102. package/dist/server/jobs/logger.d.ts +10 -0
  103. package/dist/server/jobs/logger.js +14 -0
  104. package/dist/server/jobs/plainjobAdapter.d.ts +19 -0
  105. package/dist/server/jobs/plainjobAdapter.js +48 -0
  106. package/dist/server/jobs/polling.d.ts +17 -0
  107. package/dist/server/jobs/polling.js +79 -0
  108. package/dist/server/jobs/runner.d.ts +24 -0
  109. package/dist/server/jobs/runner.js +186 -0
  110. package/dist/server/jobs/timeout.d.ts +28 -0
  111. package/dist/server/jobs/timeout.js +47 -0
  112. package/dist/server/jobs/types.d.ts +167 -0
  113. package/dist/server/jobs/types.js +12 -0
  114. package/dist/server/master-lock/guard.d.ts +53 -0
  115. package/dist/server/master-lock/guard.js +205 -0
  116. package/dist/server/master-lock/index.d.ts +21 -0
  117. package/dist/server/master-lock/index.js +21 -0
  118. package/dist/server/master-lock/lockPage.d.ts +34 -0
  119. package/dist/server/master-lock/lockPage.js +281 -0
  120. package/dist/server/master-lock/masterLock.d.ts +294 -0
  121. package/dist/server/master-lock/masterLock.js +523 -0
  122. package/dist/server/master-lock/principals.d.ts +76 -0
  123. package/dist/server/master-lock/principals.js +157 -0
  124. package/dist/server/master-lock/seed.d.ts +47 -0
  125. package/dist/server/master-lock/seed.js +93 -0
  126. package/dist/server/master-lock/store.d.ts +19 -0
  127. package/dist/server/master-lock/store.js +20 -0
  128. package/dist/server/media-bun/image.d.ts +7 -0
  129. package/dist/server/media-bun/image.js +96 -0
  130. package/dist/server/media-bun/index.d.ts +4 -0
  131. package/dist/server/media-bun/index.js +10 -0
  132. package/dist/server/media-bun/streaming.d.ts +86 -0
  133. package/dist/server/media-bun/streaming.js +237 -0
  134. package/dist/server/media-bun/video.d.ts +35 -0
  135. package/dist/server/media-bun/video.js +181 -0
  136. package/dist/server/metrics/metricsBuffer.d.ts +39 -0
  137. package/dist/server/metrics/metricsBuffer.js +36 -0
  138. package/dist/server/metrics/normalizeRoute.d.ts +5 -0
  139. package/dist/server/metrics/normalizeRoute.js +58 -0
  140. package/dist/server/middleware/correlationId.d.ts +9 -0
  141. package/dist/server/middleware/correlationId.js +20 -0
  142. package/dist/server/middleware/corsAllowlist.d.ts +38 -0
  143. package/dist/server/middleware/corsAllowlist.js +37 -0
  144. package/dist/server/middleware/errorEnvelope.d.ts +17 -0
  145. package/dist/server/middleware/errorEnvelope.js +32 -0
  146. package/dist/server/middleware/index.d.ts +9 -0
  147. package/dist/server/middleware/index.js +10 -0
  148. package/dist/server/middleware/localOrigin.d.ts +71 -0
  149. package/dist/server/middleware/localOrigin.js +119 -0
  150. package/dist/server/middleware/rateLimit.d.ts +56 -0
  151. package/dist/server/middleware/rateLimit.js +86 -0
  152. package/dist/server/middleware/requestLogger.d.ts +29 -0
  153. package/dist/server/middleware/requestLogger.js +47 -0
  154. package/dist/server/middleware/securityHeaders.d.ts +29 -0
  155. package/dist/server/middleware/securityHeaders.js +44 -0
  156. package/dist/server/middleware/zValidator.d.ts +29 -0
  157. package/dist/server/middleware/zValidator.js +57 -0
  158. package/dist/server/migration/runner.d.ts +42 -0
  159. package/dist/server/migration/runner.js +70 -0
  160. package/dist/server/migration/types.d.ts +16 -0
  161. package/dist/server/migration/types.js +1 -0
  162. package/dist/server/net/outboundGuard.d.ts +30 -0
  163. package/dist/server/net/outboundGuard.js +199 -0
  164. package/dist/server/notifications/hub.d.ts +67 -0
  165. package/dist/server/notifications/hub.js +187 -0
  166. package/dist/server/notifications/index.d.ts +47 -0
  167. package/dist/server/notifications/index.js +46 -0
  168. package/dist/server/notifications/migrations.d.ts +13 -0
  169. package/dist/server/notifications/migrations.js +20 -0
  170. package/dist/server/notifications/routes.d.ts +58 -0
  171. package/dist/server/notifications/routes.js +113 -0
  172. package/dist/server/notifications/service.d.ts +58 -0
  173. package/dist/server/notifications/service.js +41 -0
  174. package/dist/server/paths/appDataHome.d.ts +19 -0
  175. package/dist/server/paths/appDataHome.js +68 -0
  176. package/dist/server/private-media/crypto.d.ts +37 -0
  177. package/dist/server/private-media/crypto.js +111 -0
  178. package/dist/server/private-media/index-store.d.ts +31 -0
  179. package/dist/server/private-media/index-store.js +36 -0
  180. package/dist/server/private-media/mountPrivateRoutes.d.ts +26 -0
  181. package/dist/server/private-media/mountPrivateRoutes.js +219 -0
  182. package/dist/server/private-media/pipeline.d.ts +37 -0
  183. package/dist/server/private-media/pipeline.js +97 -0
  184. package/dist/server/private-media/unlock.d.ts +52 -0
  185. package/dist/server/private-media/unlock.js +55 -0
  186. package/dist/server/resume/index.d.ts +2 -0
  187. package/dist/server/resume/index.js +2 -0
  188. package/dist/server/resume/routes.d.ts +41 -0
  189. package/dist/server/resume/routes.js +50 -0
  190. package/dist/server/resume/store.d.ts +37 -0
  191. package/dist/server/resume/store.js +38 -0
  192. package/dist/server/retention/archive.d.ts +78 -0
  193. package/dist/server/retention/archive.js +284 -0
  194. package/dist/server/retention/fleet.d.ts +50 -0
  195. package/dist/server/retention/fleet.js +66 -0
  196. package/dist/server/retention/index.d.ts +38 -0
  197. package/dist/server/retention/index.js +38 -0
  198. package/dist/server/retention/policy.d.ts +263 -0
  199. package/dist/server/retention/policy.js +264 -0
  200. package/dist/server/retention/store.d.ts +101 -0
  201. package/dist/server/retention/store.js +251 -0
  202. package/dist/server/retention/sweeper.d.ts +69 -0
  203. package/dist/server/retention/sweeper.js +289 -0
  204. package/dist/server/retention/target.d.ts +143 -0
  205. package/dist/server/retention/target.js +438 -0
  206. package/dist/server/search/index.d.ts +2 -0
  207. package/dist/server/search/index.js +6 -0
  208. package/dist/server/search/pgSearchProvider.d.ts +91 -0
  209. package/dist/server/search/pgSearchProvider.js +330 -0
  210. package/dist/server/search/pgSearchProvider.pg.test.d.ts +1 -0
  211. package/dist/server/search/pgSearchProvider.pg.test.js +338 -0
  212. package/dist/server/search/pgSearchSchema.d.ts +81 -0
  213. package/dist/server/search/pgSearchSchema.js +89 -0
  214. package/dist/server/sharing/index.d.ts +18 -0
  215. package/dist/server/sharing/index.js +16 -0
  216. package/dist/server/sharing/migrations.d.ts +6 -0
  217. package/dist/server/sharing/migrations.js +79 -0
  218. package/dist/server/sharing/router.d.ts +111 -0
  219. package/dist/server/sharing/router.js +93 -0
  220. package/dist/server/sharing/sql.d.ts +67 -0
  221. package/dist/server/sharing/sql.js +88 -0
  222. package/dist/server/sharing/store.d.ts +173 -0
  223. package/dist/server/sharing/store.js +484 -0
  224. package/dist/server/sqlite/backup.d.ts +48 -0
  225. package/dist/server/sqlite/backup.js +97 -0
  226. package/dist/server/sqlite/browse.d.ts +42 -0
  227. package/dist/server/sqlite/browse.js +84 -0
  228. package/dist/server/sqlite/export.d.ts +32 -0
  229. package/dist/server/sqlite/export.js +42 -0
  230. package/dist/server/sqlite/fts5.d.ts +106 -0
  231. package/dist/server/sqlite/fts5.js +154 -0
  232. package/dist/server/sqlite/health.d.ts +44 -0
  233. package/dist/server/sqlite/health.js +52 -0
  234. package/dist/server/sqlite/import.d.ts +36 -0
  235. package/dist/server/sqlite/import.js +108 -0
  236. package/dist/server/sqlite/index.d.ts +9 -0
  237. package/dist/server/sqlite/index.js +15 -0
  238. package/dist/server/sqlite/readonlySql.d.ts +15 -0
  239. package/dist/server/sqlite/readonlySql.js +65 -0
  240. package/dist/server/sqlite/stats.d.ts +22 -0
  241. package/dist/server/sqlite/stats.js +32 -0
  242. package/dist/server/sqlite/viewer.d.ts +17 -0
  243. package/dist/server/sqlite/viewer.js +56 -0
  244. package/dist/server/storage/binaryServerProcessing.d.ts +23 -0
  245. package/dist/server/storage/binaryServerProcessing.js +81 -0
  246. package/dist/server/storage/binaryServerStore.d.ts +3 -0
  247. package/dist/server/storage/binaryServerStore.js +95 -0
  248. package/dist/server/storage/diskStore.d.ts +2 -0
  249. package/dist/server/storage/diskStore.js +88 -0
  250. package/dist/server/storage/files/adapter.d.ts +33 -0
  251. package/dist/server/storage/files/adapter.js +203 -0
  252. package/dist/server/storage/files/catalogue.d.ts +72 -0
  253. package/dist/server/storage/files/catalogue.js +1033 -0
  254. package/dist/server/storage/files/chunkedUpload.d.ts +56 -0
  255. package/dist/server/storage/files/chunkedUpload.js +70 -0
  256. package/dist/server/storage/files/fileRoutes.d.ts +51 -0
  257. package/dist/server/storage/files/fileRoutes.js +405 -0
  258. package/dist/server/storage/files/index.d.ts +9 -0
  259. package/dist/server/storage/files/index.js +11 -0
  260. package/dist/server/storage/files/migrations.d.ts +2 -0
  261. package/dist/server/storage/files/migrations.js +163 -0
  262. package/dist/server/storage/files/mimeByExt.d.ts +1 -0
  263. package/dist/server/storage/files/mimeByExt.js +13 -0
  264. package/dist/server/storage/files/stack.d.ts +136 -0
  265. package/dist/server/storage/files/stack.js +132 -0
  266. package/dist/server/storage/files/storageOps.d.ts +54 -0
  267. package/dist/server/storage/files/storageOps.js +110 -0
  268. package/dist/server/storage/files/types.d.ts +261 -0
  269. package/dist/server/storage/files/types.js +22 -0
  270. package/dist/server/storage/imageThumbnail.d.ts +3 -0
  271. package/dist/server/storage/imageThumbnail.js +50 -0
  272. package/dist/server/storage/index.d.ts +15 -0
  273. package/dist/server/storage/index.js +27 -0
  274. package/dist/server/storage/mountMediaRoutes.d.ts +28 -0
  275. package/dist/server/storage/mountMediaRoutes.js +141 -0
  276. package/dist/server/storage/objectStoreMediaStore.d.ts +4 -0
  277. package/dist/server/storage/objectStoreMediaStore.js +57 -0
  278. package/dist/server/storage/s3ObjectStore.d.ts +63 -0
  279. package/dist/server/storage/s3ObjectStore.js +187 -0
  280. package/dist/server/storage/sources.d.ts +47 -0
  281. package/dist/server/storage/sources.js +148 -0
  282. package/dist/server/storage/streamRange.d.ts +19 -0
  283. package/dist/server/storage/streamRange.js +69 -0
  284. package/dist/server/storage/streamToken.d.ts +16 -0
  285. package/dist/server/storage/streamToken.js +54 -0
  286. package/dist/server/storage/transcodeJobs.d.ts +65 -0
  287. package/dist/server/storage/transcodeJobs.js +104 -0
  288. package/dist/server/storage/types.d.ts +193 -0
  289. package/dist/server/storage/types.js +41 -0
  290. package/dist/server/storage/videoDerivativeKeys.d.ts +127 -0
  291. package/dist/server/storage/videoDerivativeKeys.js +149 -0
  292. package/dist/server/storage/videoProcessor.d.ts +30 -0
  293. package/dist/server/storage/videoProcessor.js +148 -0
  294. package/dist/server/sync/alarm.d.ts +34 -0
  295. package/dist/server/sync/alarm.js +91 -0
  296. package/dist/server/sync/commands.d.ts +87 -0
  297. package/dist/server/sync/commands.js +241 -0
  298. package/dist/server/sync/engine.d.ts +62 -0
  299. package/dist/server/sync/engine.js +184 -0
  300. package/dist/server/sync/http.d.ts +106 -0
  301. package/dist/server/sync/http.js +243 -0
  302. package/dist/server/sync/index.d.ts +41 -0
  303. package/dist/server/sync/index.js +41 -0
  304. package/dist/server/sync/opLog.d.ts +61 -0
  305. package/dist/server/sync/opLog.js +96 -0
  306. package/dist/server/sync/planner.d.ts +31 -0
  307. package/dist/server/sync/planner.js +30 -0
  308. package/dist/server/sync/status.d.ts +63 -0
  309. package/dist/server/sync/status.js +49 -0
  310. package/dist/server/sync/timer.d.ts +52 -0
  311. package/dist/server/sync/timer.js +170 -0
  312. package/dist/server/sync/tokens.d.ts +25 -0
  313. package/dist/server/sync/tokens.js +51 -0
  314. package/dist/server/sync/types.d.ts +101 -0
  315. package/dist/server/sync/types.js +29 -0
  316. package/dist/server/ws/eventBus.d.ts +14 -0
  317. package/dist/server/ws/eventBus.js +42 -0
  318. package/dist/server/ws/hub.d.ts +20 -0
  319. package/dist/server/ws/hub.js +154 -0
  320. package/dist/server/ws/index.d.ts +5 -0
  321. package/dist/server/ws/index.js +7 -0
  322. package/dist/server/ws/presence.d.ts +11 -0
  323. package/dist/server/ws/presence.js +20 -0
  324. package/dist/server/ws/room.d.ts +17 -0
  325. package/dist/server/ws/room.js +23 -0
  326. package/dist/server/ws/types.d.ts +122 -0
  327. package/dist/server/ws/types.js +1 -0
  328. package/package.json +234 -0
  329. package/src/declaredDepsAreImported.spec.ts +87 -0
  330. package/src/leafSubpathsImportNothing.spec.ts +358 -0
  331. package/src/lockfileIsTracked.spec.ts +50 -0
  332. package/src/namedSubpathsResolve.spec.ts +122 -0
  333. package/src/noPathDeps.spec.ts +24 -0
  334. package/src/publishShape.spec.ts +89 -0
  335. package/src/server/activity/index.ts +59 -0
  336. package/src/server/activity/migrations.ts +67 -0
  337. package/src/server/activity/shareSource.ts +57 -0
  338. package/src/server/activity/store.spec.ts +255 -0
  339. package/src/server/activity/store.ts +368 -0
  340. package/src/server/app.spec.ts +243 -0
  341. package/src/server/app.ts +89 -0
  342. package/src/server/audit/index.ts +2 -0
  343. package/src/server/audit/migrations.ts +88 -0
  344. package/src/server/audit/sqliteSink.test.ts +200 -0
  345. package/src/server/audit/sqliteSink.ts +162 -0
  346. package/src/server/auth/cookie.spec.ts +46 -0
  347. package/src/server/auth/cookie.ts +42 -0
  348. package/src/server/auth/csrf.spec.ts +81 -0
  349. package/src/server/auth/csrf.ts +83 -0
  350. package/src/server/auth/fileTokenSigner.ts +40 -0
  351. package/src/server/auth/getOnly.spec.ts +53 -0
  352. package/src/server/auth/getOnly.ts +32 -0
  353. package/src/server/auth/index.ts +51 -0
  354. package/src/server/auth/jwt.spec.ts +106 -0
  355. package/src/server/auth/jwt.ts +75 -0
  356. package/src/server/auth/loginThrottle.spec.ts +213 -0
  357. package/src/server/auth/loginThrottle.ts +261 -0
  358. package/src/server/auth/middleware.spec.ts +148 -0
  359. package/src/server/auth/middleware.ts +56 -0
  360. package/src/server/auth/password.spec.ts +48 -0
  361. package/src/server/auth/password.ts +71 -0
  362. package/src/server/auth/sessionStore.spec.ts +189 -0
  363. package/src/server/auth/sessionStore.ts +261 -0
  364. package/src/server/auth/totp.spec.ts +73 -0
  365. package/src/server/auth/totp.ts +92 -0
  366. package/src/server/auth/types.ts +146 -0
  367. package/src/server/auth/vault.spec.ts +51 -0
  368. package/src/server/auth/vault.ts +71 -0
  369. package/src/server/context.ts +37 -0
  370. package/src/server/ctgr.spec.ts +138 -0
  371. package/src/server/ctgr.ts +108 -0
  372. package/src/server/db/kysely.spec.ts +62 -0
  373. package/src/server/db/kysely.ts +21 -0
  374. package/src/server/db/plumbingTypes.ts +126 -0
  375. package/src/server/dropzone/duplicates.ts +121 -0
  376. package/src/server/dropzone/index.ts +30 -0
  377. package/src/server/dropzone/scan.spec.ts +255 -0
  378. package/src/server/dropzone/scan.ts +342 -0
  379. package/src/server/errors.spec.ts +76 -0
  380. package/src/server/errors.ts +22 -0
  381. package/src/server/guard/argon2Binding.spec.ts +117 -0
  382. package/src/server/guard/argon2Binding.ts +195 -0
  383. package/src/server/guard/argon2Kat.spec.ts +250 -0
  384. package/src/server/guard/guard.spec.ts +390 -0
  385. package/src/server/guard/index.ts +6 -0
  386. package/src/server/guard/kdfWorker.spec.ts +283 -0
  387. package/src/server/guard/kdfWorker.ts +367 -0
  388. package/src/server/guard/kdfWorkerEntry.ts +16 -0
  389. package/src/server/guard/passwordVerifier.spec.ts +71 -0
  390. package/src/server/guard/passwordVerifier.ts +175 -0
  391. package/src/server/guard/revocationStore.spec.ts +135 -0
  392. package/src/server/guard/revocationStore.ts +291 -0
  393. package/src/server/guard/sessionSigner.ts +425 -0
  394. package/src/server/guard/verifier.ts +27 -0
  395. package/src/server/incidents/channels.ts +487 -0
  396. package/src/server/incidents/checks.ts +120 -0
  397. package/src/server/incidents/incidents.spec.ts +463 -0
  398. package/src/server/incidents/index.ts +96 -0
  399. package/src/server/incidents/migrations.ts +66 -0
  400. package/src/server/incidents/notifier.ts +215 -0
  401. package/src/server/incidents/reporter.ts +110 -0
  402. package/src/server/incidents/routes.ts +314 -0
  403. package/src/server/incidents/store.ts +329 -0
  404. package/src/server/incidents/types.ts +116 -0
  405. package/src/server/index.ts +248 -0
  406. package/src/server/jobs/fireQueue.spec.ts +85 -0
  407. package/src/server/jobs/fireQueue.ts +37 -0
  408. package/src/server/jobs/index.ts +26 -0
  409. package/src/server/jobs/logger.ts +16 -0
  410. package/src/server/jobs/plainjobAdapter.spec.ts +34 -0
  411. package/src/server/jobs/plainjobAdapter.ts +54 -0
  412. package/src/server/jobs/polling.spec.ts +219 -0
  413. package/src/server/jobs/polling.ts +81 -0
  414. package/src/server/jobs/runner.spec.ts +213 -0
  415. package/src/server/jobs/runner.ts +213 -0
  416. package/src/server/jobs/timeout.spec.ts +35 -0
  417. package/src/server/jobs/timeout.ts +53 -0
  418. package/src/server/jobs/types.ts +188 -0
  419. package/src/server/master-lock/guard.spec.ts +347 -0
  420. package/src/server/master-lock/guard.ts +290 -0
  421. package/src/server/master-lock/index.ts +44 -0
  422. package/src/server/master-lock/lockPage.spec.ts +88 -0
  423. package/src/server/master-lock/lockPage.ts +313 -0
  424. package/src/server/master-lock/masterLock.spec.ts +338 -0
  425. package/src/server/master-lock/masterLock.ts +627 -0
  426. package/src/server/master-lock/principals.spec.ts +378 -0
  427. package/src/server/master-lock/principals.ts +175 -0
  428. package/src/server/master-lock/seed.spec.ts +69 -0
  429. package/src/server/master-lock/seed.ts +112 -0
  430. package/src/server/master-lock/store.ts +35 -0
  431. package/src/server/media-bun/image.spec.ts +115 -0
  432. package/src/server/media-bun/image.ts +134 -0
  433. package/src/server/media-bun/index.ts +52 -0
  434. package/src/server/media-bun/streaming.spec.ts +134 -0
  435. package/src/server/media-bun/streaming.ts +347 -0
  436. package/src/server/media-bun/video.spec.ts +115 -0
  437. package/src/server/media-bun/video.ts +232 -0
  438. package/src/server/metrics/metricsBuffer.ts +92 -0
  439. package/src/server/metrics/normalizeRoute.spec.ts +51 -0
  440. package/src/server/metrics/normalizeRoute.ts +63 -0
  441. package/src/server/middleware/correlationId.ts +24 -0
  442. package/src/server/middleware/corsAllowlist.spec.ts +118 -0
  443. package/src/server/middleware/corsAllowlist.ts +69 -0
  444. package/src/server/middleware/errorEnvelope.ts +38 -0
  445. package/src/server/middleware/index.ts +19 -0
  446. package/src/server/middleware/localOrigin.spec.ts +168 -0
  447. package/src/server/middleware/localOrigin.ts +139 -0
  448. package/src/server/middleware/rateLimit.spec.ts +72 -0
  449. package/src/server/middleware/rateLimit.ts +125 -0
  450. package/src/server/middleware/requestLogger.ts +95 -0
  451. package/src/server/middleware/securityHeaders.ts +78 -0
  452. package/src/server/middleware/zValidator.spec.ts +251 -0
  453. package/src/server/middleware/zValidator.ts +114 -0
  454. package/src/server/migration/runner.spec.ts +88 -0
  455. package/src/server/migration/runner.ts +87 -0
  456. package/src/server/migration/types.ts +17 -0
  457. package/src/server/net/outboundGuard.spec.ts +90 -0
  458. package/src/server/net/outboundGuard.ts +205 -0
  459. package/src/server/notifications/hub.ts +245 -0
  460. package/src/server/notifications/index.ts +77 -0
  461. package/src/server/notifications/migrations.ts +39 -0
  462. package/src/server/notifications/routes.ts +177 -0
  463. package/src/server/notifications/service.spec.ts +305 -0
  464. package/src/server/notifications/service.ts +112 -0
  465. package/src/server/paths/appDataHome.spec.ts +87 -0
  466. package/src/server/paths/appDataHome.ts +84 -0
  467. package/src/server/private-media/blobRoute.spec.ts +165 -0
  468. package/src/server/private-media/crypto.spec.ts +81 -0
  469. package/src/server/private-media/crypto.ts +155 -0
  470. package/src/server/private-media/gatePolicy.spec.ts +98 -0
  471. package/src/server/private-media/index-store.ts +83 -0
  472. package/src/server/private-media/mountPrivateRoutes.ts +265 -0
  473. package/src/server/private-media/pipeline.spec.ts +111 -0
  474. package/src/server/private-media/pipeline.ts +170 -0
  475. package/src/server/private-media/routes.spec.ts +265 -0
  476. package/src/server/private-media/unlock.ts +112 -0
  477. package/src/server/resume/index.ts +8 -0
  478. package/src/server/resume/resume.spec.ts +118 -0
  479. package/src/server/resume/routes.ts +92 -0
  480. package/src/server/resume/store.ts +82 -0
  481. package/src/server/retention/archive.spec.ts +181 -0
  482. package/src/server/retention/archive.ts +353 -0
  483. package/src/server/retention/fleet.ts +104 -0
  484. package/src/server/retention/index.ts +101 -0
  485. package/src/server/retention/policy.spec.ts +132 -0
  486. package/src/server/retention/policy.ts +392 -0
  487. package/src/server/retention/store.spec.ts +236 -0
  488. package/src/server/retention/store.ts +338 -0
  489. package/src/server/retention/sweeper.spec.ts +321 -0
  490. package/src/server/retention/sweeper.ts +367 -0
  491. package/src/server/retention/target.spec.ts +592 -0
  492. package/src/server/retention/target.ts +613 -0
  493. package/src/server/search/index.ts +12 -0
  494. package/src/server/search/pgSearchProvider.pg.test.ts +392 -0
  495. package/src/server/search/pgSearchProvider.ts +421 -0
  496. package/src/server/search/pgSearchSchema.ts +147 -0
  497. package/src/server/sharing/index.ts +65 -0
  498. package/src/server/sharing/migrations.ts +128 -0
  499. package/src/server/sharing/router.spec.ts +167 -0
  500. package/src/server/sharing/router.ts +194 -0
  501. package/src/server/sharing/sql.ts +109 -0
  502. package/src/server/sharing/store.spec.ts +408 -0
  503. package/src/server/sharing/store.ts +737 -0
  504. package/src/server/sqlite/backup.spec.ts +114 -0
  505. package/src/server/sqlite/backup.ts +154 -0
  506. package/src/server/sqlite/browse.spec.ts +70 -0
  507. package/src/server/sqlite/browse.ts +120 -0
  508. package/src/server/sqlite/export.spec.ts +43 -0
  509. package/src/server/sqlite/export.ts +74 -0
  510. package/src/server/sqlite/fts5.spec.ts +175 -0
  511. package/src/server/sqlite/fts5.ts +255 -0
  512. package/src/server/sqlite/health.spec.ts +79 -0
  513. package/src/server/sqlite/health.ts +105 -0
  514. package/src/server/sqlite/import.spec.ts +75 -0
  515. package/src/server/sqlite/import.ts +141 -0
  516. package/src/server/sqlite/index.ts +64 -0
  517. package/src/server/sqlite/readonlySql.spec.ts +58 -0
  518. package/src/server/sqlite/readonlySql.ts +67 -0
  519. package/src/server/sqlite/stats.spec.ts +21 -0
  520. package/src/server/sqlite/stats.ts +61 -0
  521. package/src/server/sqlite/viewer.spec.ts +127 -0
  522. package/src/server/sqlite/viewer.ts +72 -0
  523. package/src/server/storage/binaryServerProcessing.integration.spec.ts +222 -0
  524. package/src/server/storage/binaryServerProcessing.spec.ts +150 -0
  525. package/src/server/storage/binaryServerProcessing.ts +127 -0
  526. package/src/server/storage/binaryServerStore.ts +102 -0
  527. package/src/server/storage/chunked.integration.spec.ts +185 -0
  528. package/src/server/storage/diskStore.ts +83 -0
  529. package/src/server/storage/files/adapter.photo.display.spec.ts +169 -0
  530. package/src/server/storage/files/adapter.photo.spec.ts +128 -0
  531. package/src/server/storage/files/adapter.spec.ts +121 -0
  532. package/src/server/storage/files/adapter.ts +255 -0
  533. package/src/server/storage/files/catalogue.media-host.spec.ts +184 -0
  534. package/src/server/storage/files/catalogue.orderings.spec.ts +157 -0
  535. package/src/server/storage/files/catalogue.private.spec.ts +106 -0
  536. package/src/server/storage/files/catalogue.recover.spec.ts +131 -0
  537. package/src/server/storage/files/catalogue.sort.spec.ts +154 -0
  538. package/src/server/storage/files/catalogue.spec.ts +133 -0
  539. package/src/server/storage/files/catalogue.trash-import.spec.ts +125 -0
  540. package/src/server/storage/files/catalogue.ts +1326 -0
  541. package/src/server/storage/files/chunkedUpload.spec.ts +111 -0
  542. package/src/server/storage/files/chunkedUpload.ts +146 -0
  543. package/src/server/storage/files/collections.spec.ts +294 -0
  544. package/src/server/storage/files/fileRoutes.replace.spec.ts +88 -0
  545. package/src/server/storage/files/fileRoutes.spec.ts +327 -0
  546. package/src/server/storage/files/fileRoutes.ts +462 -0
  547. package/src/server/storage/files/index.ts +66 -0
  548. package/src/server/storage/files/migrations.ts +187 -0
  549. package/src/server/storage/files/mimeByExt.ts +14 -0
  550. package/src/server/storage/files/stack.spec.ts +258 -0
  551. package/src/server/storage/files/stack.ts +284 -0
  552. package/src/server/storage/files/storageOps.spec.ts +126 -0
  553. package/src/server/storage/files/storageOps.ts +213 -0
  554. package/src/server/storage/files/types.ts +285 -0
  555. package/src/server/storage/imageThumbnail.spec.ts +81 -0
  556. package/src/server/storage/imageThumbnail.ts +60 -0
  557. package/src/server/storage/index.ts +122 -0
  558. package/src/server/storage/mountMediaRoutes.ts +231 -0
  559. package/src/server/storage/objectStore.spec.ts +272 -0
  560. package/src/server/storage/objectStoreMediaStore.ts +63 -0
  561. package/src/server/storage/s3ObjectStore.ts +239 -0
  562. package/src/server/storage/sources.ts +245 -0
  563. package/src/server/storage/storage.spec.ts +219 -0
  564. package/src/server/storage/streamRange.ts +92 -0
  565. package/src/server/storage/streamToken.ts +76 -0
  566. package/src/server/storage/transcode.integration.spec.ts +134 -0
  567. package/src/server/storage/transcodeJobs.spec.ts +152 -0
  568. package/src/server/storage/transcodeJobs.ts +171 -0
  569. package/src/server/storage/types.ts +230 -0
  570. package/src/server/storage/videoDerivativeKeys.spec.ts +112 -0
  571. package/src/server/storage/videoDerivativeKeys.ts +172 -0
  572. package/src/server/storage/videoProcessor.posterOnly.spec.ts +106 -0
  573. package/src/server/storage/videoProcessor.ts +229 -0
  574. package/src/server/sync/alarm.spec.ts +149 -0
  575. package/src/server/sync/alarm.ts +137 -0
  576. package/src/server/sync/commands.spec.ts +145 -0
  577. package/src/server/sync/commands.ts +361 -0
  578. package/src/server/sync/engine.spec.ts +496 -0
  579. package/src/server/sync/engine.ts +255 -0
  580. package/src/server/sync/http.ts +316 -0
  581. package/src/server/sync/httpRemote.spec.ts +158 -0
  582. package/src/server/sync/index.ts +90 -0
  583. package/src/server/sync/opLog.spec.ts +110 -0
  584. package/src/server/sync/opLog.ts +189 -0
  585. package/src/server/sync/planner.spec.ts +53 -0
  586. package/src/server/sync/planner.ts +62 -0
  587. package/src/server/sync/status.spec.ts +92 -0
  588. package/src/server/sync/status.ts +108 -0
  589. package/src/server/sync/timer.spec.ts +150 -0
  590. package/src/server/sync/timer.ts +207 -0
  591. package/src/server/sync/tokens.spec.ts +38 -0
  592. package/src/server/sync/tokens.ts +94 -0
  593. package/src/server/sync/types.ts +108 -0
  594. package/src/server/ws/eventBus.spec.ts +67 -0
  595. package/src/server/ws/eventBus.ts +46 -0
  596. package/src/server/ws/hub.integration.spec.ts +93 -0
  597. package/src/server/ws/hub.spec.ts +282 -0
  598. package/src/server/ws/hub.ts +168 -0
  599. package/src/server/ws/index.ts +20 -0
  600. package/src/server/ws/presence.spec.ts +27 -0
  601. package/src/server/ws/presence.ts +26 -0
  602. package/src/server/ws/room.spec.ts +36 -0
  603. package/src/server/ws/room.ts +29 -0
  604. package/src/server/ws/types.ts +134 -0
  605. package/src/skippedTestsAreAnswered.spec.ts +215 -0
  606. package/src/testTmpRoot.spec.ts +102 -0
@@ -0,0 +1,135 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import {
3
+ createAsyncBackedRevocationStore,
4
+ createMemoryAsyncRevocationStore,
5
+ createMemoryRevocationStore,
6
+ createSqliteRevocationStore,
7
+ } from './revocationStore';
8
+
9
+ describe('sync memory revocation store', () => {
10
+ it('records, answers, and sweeps by ms expiry', () => {
11
+ const store = createMemoryRevocationStore();
12
+ expect(store.isRevoked('a')).toBe(false);
13
+ store.revoke('a', 10_000);
14
+ expect(store.isRevoked('a')).toBe(true);
15
+ expect(store.size()).toBe(1);
16
+ // sweep drops entries whose expiry has passed, keeps the rest
17
+ store.revoke('b', 30_000);
18
+ store.sweep(20_000);
19
+ expect(store.isRevoked('a')).toBe(false);
20
+ expect(store.isRevoked('b')).toBe(true);
21
+ expect(store.size()).toBe(1);
22
+ });
23
+ });
24
+
25
+ describe('sqlite revocation store', () => {
26
+ it('records, answers, sweeps, and closes', () => {
27
+ const store = createSqliteRevocationStore(':memory:');
28
+ try {
29
+ store.revoke('sess-1', 50_000);
30
+ expect(store.isRevoked('sess-1')).toBe(true);
31
+ expect(store.isRevoked('sess-2')).toBe(false);
32
+ store.sweep(60_000); // past the expiry → gone
33
+ expect(store.isRevoked('sess-1')).toBe(false);
34
+ } finally {
35
+ store.close();
36
+ }
37
+ });
38
+
39
+ it('re-revoking an id is idempotent (INSERT OR REPLACE)', () => {
40
+ const store = createSqliteRevocationStore(':memory:');
41
+ try {
42
+ store.revoke('id', 10_000);
43
+ store.revoke('id', 20_000);
44
+ expect(store.isRevoked('id')).toBe(true);
45
+ store.sweep(15_000); // the second, later expiry wins → still revoked
46
+ expect(store.isRevoked('id')).toBe(true);
47
+ } finally {
48
+ store.close();
49
+ }
50
+ });
51
+ });
52
+
53
+ describe('async-backed revocation store (the cross-node bridge)', () => {
54
+ it('refuses an id synchronously the instant revoke() returns, before the write settles', () => {
55
+ const backing = createMemoryAsyncRevocationStore();
56
+ let clock = 1_000_000;
57
+ const store = createAsyncBackedRevocationStore(backing, { now: () => clock });
58
+ store.revoke('jti', clock + 60_000);
59
+ // No await: the local cache must already refuse it.
60
+ expect(store.isRevoked('jti')).toBe(true);
61
+ });
62
+
63
+ it('a revoke on node A is seen on node B through the shared backing (V7.3-style)', async () => {
64
+ const shared = createMemoryAsyncRevocationStore();
65
+ let clock = 2_000_000;
66
+ const now = () => clock;
67
+ const nodeA = createAsyncBackedRevocationStore(shared, { now });
68
+ const nodeB = createAsyncBackedRevocationStore(shared, { now });
69
+
70
+ nodeA.revoke('jti', clock + 60_000);
71
+ await nodeA.flush(); // the write reaches the shared backing
72
+
73
+ // B has not read it yet → still allowed. The read is scheduled, not awaited.
74
+ expect(nodeB.isRevoked('jti')).toBe(false);
75
+ await nodeB.flush(); // the scheduled read settles → B has learned it
76
+ expect(nodeB.isRevoked('jti')).toBe(true);
77
+
78
+ // ms→s conversion round-trips: the backing holds epoch SECONDS.
79
+ expect(await shared.get('jti')).toBe(Math.ceil((clock + 60_000) / 1000));
80
+ });
81
+
82
+ it('a stale cache entry triggers a background refresh past cacheTtlMs', async () => {
83
+ const shared = createMemoryAsyncRevocationStore();
84
+ let clock = 3_000_000;
85
+ const reader = createAsyncBackedRevocationStore(shared, { now: () => clock, cacheTtlMs: 5_000 });
86
+
87
+ expect(reader.isRevoked('jti')).toBe(false); // schedules first read (miss)
88
+ await reader.flush();
89
+
90
+ // A peer revokes directly in the shared backing.
91
+ await shared.set('jti', Math.ceil((clock + 60_000) / 1000));
92
+
93
+ // Within the cache window the reader still trusts its cached miss.
94
+ clock += 4_000;
95
+ expect(reader.isRevoked('jti')).toBe(false);
96
+
97
+ // Past the window it re-reads.
98
+ clock += 2_000;
99
+ expect(reader.isRevoked('jti')).toBe(false); // returns cached, schedules refresh
100
+ await reader.flush();
101
+ expect(reader.isRevoked('jti')).toBe(true);
102
+ });
103
+
104
+ it('sweep prunes the shared backing and drops expired local cache entries', async () => {
105
+ const shared = createMemoryAsyncRevocationStore();
106
+ let clock = 4_000_000;
107
+ const store = createAsyncBackedRevocationStore(shared, { now: () => clock });
108
+ store.revoke('old', clock + 1_000);
109
+ store.revoke('new', clock + 60_000);
110
+ await store.flush();
111
+ expect(shared.size()).toBe(2);
112
+
113
+ clock += 30_000;
114
+ store.sweep(clock);
115
+ await store.flush();
116
+ expect(shared.size()).toBe(1); // 'old' pruned from the shared backing
117
+ expect(store.isRevoked('new')).toBe(true);
118
+ });
119
+
120
+ it('a backing failure is reported, never thrown out of the request path', async () => {
121
+ const ops: string[] = [];
122
+ const boom: import('./revocationStore').AsyncRevocationStore = {
123
+ set: () => Promise.reject(new Error('set failed')),
124
+ get: () => Promise.reject(new Error('get failed')),
125
+ prune: () => Promise.reject(new Error('prune failed')),
126
+ };
127
+ const store = createAsyncBackedRevocationStore(boom, { onError: (op) => ops.push(op) });
128
+ expect(() => store.revoke('x', 10_000)).not.toThrow();
129
+ // A different, uncached id so `isRevoked` actually schedules a backing read.
130
+ expect(() => store.isRevoked('y')).not.toThrow();
131
+ expect(() => store.sweep(10_000)).not.toThrow();
132
+ await store.flush();
133
+ expect(ops.sort()).toEqual(['get', 'prune', 'set']);
134
+ });
135
+ });
@@ -0,0 +1,291 @@
1
+ /**
2
+ * The one revocation store, in two shapes for two hot paths (DEDUP-7).
3
+ *
4
+ * "Was this session/token withdrawn early?" is a single concern, but it is
5
+ * consulted from two verify paths whose constraints do not meet in the middle,
6
+ * so this file settles ONE contract per constraint and gives the async one a
7
+ * bridge back to the sync one:
8
+ *
9
+ * • {@link RevocationStore} — SYNCHRONOUS (`isRevoked`/`revoke`/`sweep`, epoch
10
+ * **milliseconds**). The single-owner satellites' `createOwnerAuth` consults
11
+ * it inside `isAuthenticated`, which is called on every request and cannot
12
+ * become a promise without turning every route async. Its durable
13
+ * implementation is a local sqlite file — the denylist a redeploy must not
14
+ * forget (`apps/roms`'s deploy smoke caught a logout that did not log out).
15
+ *
16
+ * • {@link AsyncRevocationStore} — ASYNCHRONOUS (`set`/`get`/`prune`, epoch
17
+ * **seconds**). The cross-node seam (03 §4a, audit G6): a Redis hash or a
18
+ * Postgres row that every node in a saas deployment reads, so a `lock()` on
19
+ * node A closes the token on node B too. `createSessionSigner` consumes THIS
20
+ * one and keeps its own read-through cache so its `verify` stays sync.
21
+ *
22
+ * Before DEDUP-7 these lived in two packages, BOTH exporting a
23
+ * `createMemoryRevocationStore` of a different shape — a live footgun the moment
24
+ * a file imported from the wrong one. They are one file now; `satellite-kit`
25
+ * re-exports the sync half so its callers are unchanged.
26
+ *
27
+ * The bridge between them is {@link createAsyncBackedRevocationStore}: a SYNC
28
+ * store whose durable backing is an async cross-node store, using the same
29
+ * read-through-cache trick the signer does. It is what lets a satellite opt into
30
+ * the cross-node seam without its `isAuthenticated` becoming a promise.
31
+ */
32
+ import { mkdirSync } from 'node:fs';
33
+ import { dirname } from 'node:path';
34
+ import { Database } from 'bun:sqlite';
35
+ import { assertCcPragmas } from 'cwip/sqlite';
36
+
37
+ /* ============================================================ sync contract */
38
+
39
+ /**
40
+ * Somewhere to record the session/token ids that must no longer be accepted,
41
+ * answered synchronously.
42
+ *
43
+ * Deliberately tiny, and deliberately not a session store: the token still
44
+ * carries its own expiry and signature, so this only ever answers "was this one
45
+ * withdrawn early". `sweep` exists so the table cannot grow forever — every
46
+ * entry becomes meaningless the moment its session would have expired anyway.
47
+ *
48
+ * Epoch **milliseconds** throughout, matching `Date.now()` and the satellites'
49
+ * cookie expiry. (The cross-node {@link AsyncRevocationStore} works in seconds,
50
+ * matching the signer's `exp`; {@link createAsyncBackedRevocationStore} bridges
51
+ * the two.)
52
+ */
53
+ export interface RevocationStore {
54
+ /** Has this id been revoked? Called on every authenticated request. */
55
+ isRevoked(id: string): boolean;
56
+ /** Record a revocation until `expiresAtMs`, after which it is moot. */
57
+ revoke(id: string, expiresAtMs: number): void;
58
+ /** Drop entries whose sessions have expired. Called opportunistically. */
59
+ sweep(nowMs: number): void;
60
+ }
61
+
62
+ /**
63
+ * An in-memory sync {@link RevocationStore}.
64
+ *
65
+ * For tests, and for a single-process caller that does not need durability.
66
+ * NOT for a deployed satellite: an in-memory table would be worse than none —
67
+ * sign out, redeploy, and the session works again, which reads as the feature
68
+ * being broken rather than absent. Give a deployed door
69
+ * {@link createSqliteRevocationStore} (durable) or nothing.
70
+ */
71
+ export function createMemoryRevocationStore(): RevocationStore & { size(): number } {
72
+ const entries = new Map<string, number>();
73
+ return {
74
+ isRevoked: (id) => entries.has(id),
75
+ revoke: (id, expiresAtMs) => void entries.set(id, expiresAtMs),
76
+ sweep: (nowMs) => {
77
+ for (const [id, expiresAt] of entries) if (expiresAt <= nowMs) entries.delete(id);
78
+ },
79
+ size: () => entries.size,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * A durable sync {@link RevocationStore} backed by a sqlite file.
85
+ *
86
+ * Because an in-memory table would be worse than none (see above), and the
87
+ * deploy engine persists each release's data directory, so durability costs one
88
+ * file. The table is self-limiting: an entry is meaningless once the session it
89
+ * names would have expired anyway, and `sweep` drops those on logout — rare,
90
+ * cheap, and exactly when the table has just grown, so there is no background
91
+ * timer to own.
92
+ *
93
+ * Its OWN file, not a table in an app's database: keeping a credential surface
94
+ * out of an app's data file bounds the blast radius of a corrupt or deleted
95
+ * file to "everyone signs in again".
96
+ */
97
+ export function createSqliteRevocationStore(dbPath: string): RevocationStore & { close(): void } {
98
+ if (dbPath !== ':memory:' && !dbPath.startsWith('file:')) {
99
+ mkdirSync(dirname(dbPath), { recursive: true });
100
+ }
101
+ const db = new Database(dbPath, { create: true });
102
+ // Route pragmas through cwip's one controller (applies WAL + busy_timeout +
103
+ // foreign_keys + wal_autocheckpoint and re-reads to prove each took), rather
104
+ // than hand-rolling a lone `journal_mode = WAL` the pragma sweep rejects.
105
+ assertCcPragmas(db);
106
+ db.exec(`
107
+ CREATE TABLE IF NOT EXISTS revoked_sessions (
108
+ id TEXT PRIMARY KEY,
109
+ expires_at INTEGER NOT NULL
110
+ )
111
+ `);
112
+ const has = db.query<{ id: string }, [string]>('SELECT id FROM revoked_sessions WHERE id = ?');
113
+ const insert = db.query<unknown, [string, number]>(
114
+ 'INSERT OR REPLACE INTO revoked_sessions (id, expires_at) VALUES (?, ?)',
115
+ );
116
+ const prune = db.query<unknown, [number]>('DELETE FROM revoked_sessions WHERE expires_at <= ?');
117
+ return {
118
+ isRevoked: (id) => has.get(id) !== null,
119
+ revoke: (id, expiresAtMs) => void insert.run(id, expiresAtMs),
120
+ sweep: (nowMs) => void prune.run(nowMs),
121
+ close: () => db.close(),
122
+ };
123
+ }
124
+
125
+ /* ================================================ async cross-node contract */
126
+
127
+ /**
128
+ * Where revoked jtis live so every node sees them (03 §4a). Local profile:
129
+ * unused (a per-process `Map` is enough for one process). SaaS: a Redis hash
130
+ * with per-entry TTL, or a row in the shared Postgres — the contract is
131
+ * identical, which is the point.
132
+ *
133
+ * Only `set`/`get`/`prune` and no bulk `list`, because the consumer's hot path
134
+ * is a SYNCHRONOUS `verify` (see {@link createSessionSigner}) that consults the
135
+ * store read-through by id off the hot path — never a full scan on every verify.
136
+ *
137
+ * Epoch **seconds** throughout, matching a token's own `exp`.
138
+ */
139
+ export interface AsyncRevocationStore {
140
+ /**
141
+ * Record `jti` as revoked until `expEpochSeconds`. Idempotent; a re-revoke of
142
+ * the same jti is a no-op (or extends the exp, which is harmless — a denylist
143
+ * entry that outlives its token only wastes a little space until `prune`).
144
+ */
145
+ set(jti: string, expEpochSeconds: number): Promise<void>;
146
+ /** The `exp` of a revoked jti (epoch seconds), or `null` when it is not revoked. */
147
+ get(jti: string): Promise<number | null>;
148
+ /** Drop every entry whose `exp <= nowEpochSeconds`. Returns the count removed. */
149
+ prune(nowEpochSeconds: number): Promise<number>;
150
+ }
151
+
152
+ /**
153
+ * An in-memory {@link AsyncRevocationStore}.
154
+ *
155
+ * Two uses: a single-process saas test that wants the seam exercised without a
156
+ * real Redis, and — crucially — the **two-signer replay probe**, where one
157
+ * shared instance is handed to signer A and signer B so "revoke on A, verify on
158
+ * B" becomes provable in one process (V7.3). It is NOT a cross-process store:
159
+ * two OS processes do not share this Map, which is the whole reason a real
160
+ * deployment injects Redis/Postgres instead.
161
+ *
162
+ * (Named `…Async…` to stand apart from the sync {@link createMemoryRevocationStore};
163
+ * before DEDUP-7 both shapes shared the bare name across two packages and that
164
+ * collision was the footgun this file exists to remove.)
165
+ */
166
+ export function createMemoryAsyncRevocationStore(): AsyncRevocationStore & { size(): number } {
167
+ const revoked = new Map<string, number>();
168
+ return {
169
+ async set(jti, expEpochSeconds) {
170
+ const existing = revoked.get(jti);
171
+ // Keep the later exp: two nodes revoking the same jti with slightly
172
+ // different clocks must not shorten the window.
173
+ if (existing === undefined || expEpochSeconds > existing) revoked.set(jti, expEpochSeconds);
174
+ },
175
+ async get(jti) {
176
+ return revoked.get(jti) ?? null;
177
+ },
178
+ async prune(nowEpochSeconds) {
179
+ let removed = 0;
180
+ for (const [jti, exp] of revoked) {
181
+ if (exp <= nowEpochSeconds) {
182
+ revoked.delete(jti);
183
+ removed += 1;
184
+ }
185
+ }
186
+ return removed;
187
+ },
188
+ size() {
189
+ return revoked.size;
190
+ },
191
+ };
192
+ }
193
+
194
+ /* ==================================================== the bridge: async → sync */
195
+
196
+ export interface AsyncBackedRevocationStoreOptions {
197
+ /** Injected clock in **milliseconds** (default `Date.now`), for tests. */
198
+ now?: () => number;
199
+ /**
200
+ * How long a store verdict is trusted before `isRevoked` re-reads it (ms).
201
+ * This is the cross-node revocation LATENCY BOUND: a revocation written by
202
+ * another node is seen here within this window (plus the store's own read
203
+ * latency), because `isRevoked` stays synchronous — it answers from the cache
204
+ * and refreshes it off the hot path rather than awaiting a round trip. Default
205
+ * 5000 (03 §4a's ≤5s).
206
+ */
207
+ cacheTtlMs?: number;
208
+ /**
209
+ * Reports a backing read/write that failed. A failed write-through means a
210
+ * revocation did not reach other nodes — the local cache still refuses the id
211
+ * HERE, but the host may want to retry or alarm. Default: swallow, because a
212
+ * sync store must never throw out of `isRevoked`/`revoke` on the request path.
213
+ */
214
+ onError?: (op: 'get' | 'set' | 'prune', error: unknown) => void;
215
+ }
216
+
217
+ /**
218
+ * A SYNC {@link RevocationStore} whose durable backing is an async cross-node
219
+ * {@link AsyncRevocationStore} — the bridge that gives a satellite the cross-node
220
+ * seam without its `isAuthenticated` becoming a promise.
221
+ *
222
+ * It is the same read-through-cache trick {@link createSessionSigner} keeps
223
+ * internally, packaged so a plain sync consumer can use it: `revoke` writes
224
+ * through fire-and-forget and caches locally so the id is refused HERE the
225
+ * instant it returns; `isRevoked` answers from the cache and schedules a refresh
226
+ * when the entry is missing or older than `cacheTtlMs`, never blocking. Ids and
227
+ * expiries cross the ms↔seconds boundary here (the sync contract is ms, the
228
+ * async one seconds).
229
+ *
230
+ * {@link flush} settles the fire-and-forget work — for shutdown/handover (a node
231
+ * draining must not lose an unwritten revocation) and for tests that need the
232
+ * cross-node state to have settled.
233
+ */
234
+ export function createAsyncBackedRevocationStore(
235
+ backing: AsyncRevocationStore,
236
+ options: AsyncBackedRevocationStoreOptions = {},
237
+ ): RevocationStore & { flush(): Promise<void> } {
238
+ const now = options.now ?? (() => Date.now());
239
+ const cacheTtlMs = options.cacheTtlMs ?? 5000;
240
+ const onError = options.onError ?? (() => undefined);
241
+
242
+ /** id → the last backing verdict (exp in ms, or null) and when it was read. */
243
+ const cache = new Map<string, { expMs: number | null; readAtMs: number }>();
244
+ const pending = new Set<Promise<unknown>>();
245
+ /** Reads in flight per id, so a burst of `isRevoked` coalesces into one read. */
246
+ const inflightReads = new Map<string, Promise<void>>();
247
+
248
+ const track = (work: Promise<unknown>): void => {
249
+ pending.add(work);
250
+ void work.finally(() => pending.delete(work));
251
+ };
252
+
253
+ const refresh = (id: string): void => {
254
+ if (inflightReads.has(id)) return;
255
+ const read = backing
256
+ .get(id)
257
+ .then((expSec) => {
258
+ cache.set(id, { expMs: expSec === null ? null : expSec * 1000, readAtMs: now() });
259
+ })
260
+ .catch((error) => onError('get', error))
261
+ .finally(() => inflightReads.delete(id));
262
+ inflightReads.set(id, read);
263
+ track(read);
264
+ };
265
+
266
+ return {
267
+ isRevoked(id) {
268
+ const cached = cache.get(id);
269
+ if (cached === undefined || now() - cached.readAtMs >= cacheTtlMs) refresh(id);
270
+ // A fresh cached exp still in the future ⇒ revoked. An expired cached exp
271
+ // is treated as not-revoked: the token's own expiry already refuses it, so
272
+ // the denylist has nothing left to add.
273
+ return cached?.expMs != null && cached.expMs > now();
274
+ },
275
+ revoke(id, expiresAtMs) {
276
+ // Cache locally FIRST so this node refuses the id the instant this returns,
277
+ // regardless of whether the write-through has propagated yet.
278
+ cache.set(id, { expMs: expiresAtMs, readAtMs: now() });
279
+ track(backing.set(id, Math.ceil(expiresAtMs / 1000)).catch((error) => onError('set', error)));
280
+ },
281
+ sweep(nowMs) {
282
+ for (const [id, cached] of cache) if (cached.expMs !== null && cached.expMs <= nowMs) cache.delete(id);
283
+ track(backing.prune(Math.floor(nowMs / 1000)).catch((error) => onError('prune', error)));
284
+ },
285
+ async flush() {
286
+ // Settle in-flight work, then whatever it scheduled (a read can resolve
287
+ // after we snapshot), so the caller sees a fully-quiesced state.
288
+ while (pending.size > 0) await Promise.allSettled([...pending]);
289
+ },
290
+ };
291
+ }