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,53 @@
1
+ // cursedbelt server toolkit — native Hono on Bun.serve. Consumed by ca + ru.
2
+ //
3
+ // HTTP app + middleware + the canonical error envelope + a versioned SQLite
4
+ // migration runner + Kysely-typed access for the plumbing tables. Business routes,
5
+ // auth model, sharing, jobs, and `data JSON` tables stay in the apps; cursedbelt
6
+ // owns the plumbing.
7
+ // ── ctgr (isomorphic codec re-exports + the Hono GET-tunnel transport) ───────
8
+ export { ChunkStore, CTGR_WIRE_VERSION, decodeRequest, encodeRequest, } from 'cursedbelt-core/ctgr';
9
+ // ── HTTP app ────────────────────────────────────────────────────────────────
10
+ export { createApp, serveBun } from './app';
11
+ // ── Auth toolkit (task 26): jose JWT, cookies, CSRF, getOnly, session store, ──
12
+ // login throttle, argon2id passwords, TOTP, vault crypto, the composed middleware.
13
+ export { AUTH_COOKIE_NAME, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, clearAuthCookie, createAuthMiddleware, createCsrfMiddleware, clientKeyOf, clientKeyOfContext, createLoginThrottle, createSessionStore, createRs256FileTokenSigner, DEFAULT_TOTP_ISSUER, decryptVaultItem, deriveVaultKey, encryptVaultItem, generateRecoveryCodes, generateTotpSecret, getOnlyEnforcement, getTotpUri, hashPassword, isGetOnlyViolation, needsRehash, signToken, verifyPassword, verifyPurposeToken, verifyRecoveryCode, verifyToken, verifyTotpCode, } from './auth';
14
+ export { buildRoutes, CTGR_ROUTE_PATTERN, getCtgrDecoded, unGETify } from './ctgr';
15
+ // ── Kysely-typed plumbing tables ────────────────────────────────────────────
16
+ export { createKysely } from './db/kysely';
17
+ // ── Errors ──────────────────────────────────────────────────────────────────
18
+ export { ApiError, isApiError, toErrorEnvelope, toValidationIssues, VALIDATION_ERROR_CODE, validationEnvelope, } from './errors';
19
+ // ── Jobs & scheduler (task 29): plainjob-backed cron runner + restart-aware ───
20
+ // pollers + the due-row fire queue. App job *definitions* stay business.
21
+ export { createFireQueue, createJobRunner, createPollingJob, DEFAULT_FIRE_INTERVAL_MS, DEFAULT_JOB_TIMEOUT_MS, defaultJobLogger, JobTimeoutError, } from './jobs';
22
+ // ── Media adapter (task 19): image rendering via sharp (lazy/optional peer) + ──
23
+ // video trim / probe / thumbnail via native ffmpeg. Also at the dedicated
24
+ // media-bun entry below; sharp is imported lazily inside renderImage,
25
+ // so importing this barrel never loads the native binary.
26
+ export { buildProbeArgs, buildThumbnailArgs, buildTrimArgs, extractThumbnail, ffmpegTimeToSeconds, isFfmpegAvailable, isFfprobeAvailable, parseFfmpegProgress, parseFfprobe, probeMedia, renderImage, trimVideo, videoCodecPlan, } from './media-bun';
27
+ // ── Metrics ───────────────────────────────────────────────────────────────────
28
+ export { createMetricsBuffer, } from './metrics/metricsBuffer';
29
+ export { normalizeRoute } from './metrics/normalizeRoute';
30
+ // ── Middleware ────────────────────────────────────────────────────────────────
31
+ export { correlationId } from './middleware/correlationId';
32
+ export { corsAllowlist, corsOptsFromEnv } from './middleware/corsAllowlist';
33
+ export { errorEnvelope } from './middleware/errorEnvelope';
34
+ export { crossOriginRefusal } from './middleware/localOrigin';
35
+ export { clientIp, createRateLimiter } from './middleware/rateLimit';
36
+ export { requestLogger } from './middleware/requestLogger';
37
+ export { securityHeaders, } from './middleware/securityHeaders';
38
+ // ── zod validation toolkit ────────────────────────────────────────────────────
39
+ // (the `validationEnvelope`/`toValidationIssues`/`ValidationIssue` builders now
40
+ // live with the canonical envelope in `./errors`, re-exported above.)
41
+ export { zValidator } from './middleware/zValidator';
42
+ // ── Migration runner ──────────────────────────────────────────────────────────
43
+ export { addColumnIfMissing, applyRecommendedPragmas, columnExists, runMigrations } from './migration/runner';
44
+ // ── Audit log: the SQLite-backed AuditSink cursedshell's service dispatch ─────
45
+ // writes to (see `cwip/audit` for the storage-agnostic contract + the
46
+ // zero-config in-memory default).
47
+ export { AUDIT_LOG_MIGRATIONS, auditStateAt, auditStateAtVersion, auditStep, createSqliteAuditSink, } from './audit';
48
+ // ── SQLite toolkit extras (task 30): FTS5 ranked search + DDL generator, the ───
49
+ // read-only DB viewer middleware, the VACUUM-INTO backup service, DB health probes.
50
+ export { applyFtsSchema, buildFtsSchema, clampLimit, clampOffset, collectSqliteStats, createFts5Search, createSqliteBackupService, createSqliteHealthChecks, createSqliteViewerMiddleware, exportSqliteSnapshot, exportSqliteTable, importRowsIntoSqliteTable, isKnownTable, isReadOnlySql, listSqliteTables, NotReadOnlyError, quoteIdent, readSqliteTable, restoreSqliteSnapshot, runReadonlySqlite, sanitizeFtsQuery, snapshotSqlite, UnknownTableError, } from './sqlite';
51
+ // ── WebSocket hub (task 28): Bun-native upgrade, channel registry, heartbeat, ──
52
+ // presence-as-a-view, room helpers, and the push-only event-bus subset (ru).
53
+ export { computePresence, createEventBus, createWsHub, joinRoom, leaveRoom, ROOM_META_KEY, roomOf, } from './ws';
@@ -0,0 +1,12 @@
1
+ import { type FireQueue, type FireQueueOpts } from './types';
2
+ /**
3
+ * The "select due rows → deliver each → mark sent" pattern, as a {@link PollingJob}.
4
+ * This is ca's `event_reminders` / `alarms` delivery loop generalized: each poll
5
+ * pulls the due rows, delivers them one at a time, and marks each sent only after a
6
+ * successful delivery. A failed `deliver` routes to `onError` and is logged, and
7
+ * the loop CONTINUES with the next item — one bad row never stalls the queue.
8
+ *
9
+ * Because it fires once on boot (default), any rows that came due while the process
10
+ * was down are delivered on the first poll after restart.
11
+ */
12
+ export declare function createFireQueue<T>(opts: FireQueueOpts<T>): FireQueue;
@@ -0,0 +1,37 @@
1
+ import { defaultJobLogger } from './logger';
2
+ import { createPollingJob } from './polling';
3
+ import { DEFAULT_FIRE_INTERVAL_MS } from './types';
4
+ /**
5
+ * The "select due rows → deliver each → mark sent" pattern, as a {@link PollingJob}.
6
+ * This is ca's `event_reminders` / `alarms` delivery loop generalized: each poll
7
+ * pulls the due rows, delivers them one at a time, and marks each sent only after a
8
+ * successful delivery. A failed `deliver` routes to `onError` and is logged, and
9
+ * the loop CONTINUES with the next item — one bad row never stalls the queue.
10
+ *
11
+ * Because it fires once on boot (default), any rows that came due while the process
12
+ * was down are delivered on the first poll after restart.
13
+ */
14
+ export function createFireQueue(opts) {
15
+ const logger = opts.logger ?? defaultJobLogger;
16
+ return createPollingJob({
17
+ name: opts.name,
18
+ db: opts.db,
19
+ intervalMs: opts.intervalMs ?? DEFAULT_FIRE_INTERVAL_MS,
20
+ runOnBoot: opts.runOnBoot,
21
+ logger,
22
+ async run({ db, signal }) {
23
+ for (const item of opts.getDue(db)) {
24
+ if (signal.aborted)
25
+ break;
26
+ try {
27
+ await opts.deliver(item, db);
28
+ opts.markSent(item, db);
29
+ }
30
+ catch (err) {
31
+ opts.onError?.(item, err);
32
+ logger.error(`[fireQueue] ${opts.name} delivery failed`, err);
33
+ }
34
+ }
35
+ },
36
+ });
37
+ }
@@ -0,0 +1,6 @@
1
+ export { createFireQueue } from './fireQueue';
2
+ export { defaultJobLogger } from './logger';
3
+ export { createPollingJob } from './polling';
4
+ export { createJobRunner } from './runner';
5
+ export { JobTimeoutError } from './timeout';
6
+ export { type CreateJobRunnerOptions, DEFAULT_FIRE_INTERVAL_MS, DEFAULT_JOB_RETENTION, DEFAULT_JOB_TIMEOUT_MS, type FireQueue, type FireQueueOpts, type JobContext, type JobDef, type JobLogger, type JobRetentionOptions, type JobRunner, type JobRunRecord, type PollingJob, type PollingJobOpts, } from './types';
@@ -0,0 +1,10 @@
1
+ // cursedbelt jobs & scheduler — the in-process scheduled-work engine shared by
2
+ // ca + ru. cursedbelt owns the mechanism (durable cron scheduling via plainjob,
3
+ // restart-aware pollers, the due-row fire queue, run auditing); app job
4
+ // *definitions* stay business and consume these primitives.
5
+ export { createFireQueue } from './fireQueue';
6
+ export { defaultJobLogger } from './logger';
7
+ export { createPollingJob } from './polling';
8
+ export { createJobRunner } from './runner';
9
+ export { JobTimeoutError } from './timeout';
10
+ export { DEFAULT_FIRE_INTERVAL_MS, DEFAULT_JOB_RETENTION, DEFAULT_JOB_TIMEOUT_MS, } from './types';
@@ -0,0 +1,10 @@
1
+ import type { JobLogger } from './types';
2
+ /**
3
+ * The default job logger: warn/info/error go to the console, `debug` is a no-op.
4
+ *
5
+ * The underlying plainjob worker logs at `debug` on *every* poll ("checking for
6
+ * scheduled jobs", once per second by default), so a console `debug` would spam.
7
+ * Silencing it here keeps the default quiet while still surfacing real problems.
8
+ * Inject your own {@link JobLogger} (e.g. the app logger) to capture debug.
9
+ */
10
+ export declare const defaultJobLogger: JobLogger;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The default job logger: warn/info/error go to the console, `debug` is a no-op.
3
+ *
4
+ * The underlying plainjob worker logs at `debug` on *every* poll ("checking for
5
+ * scheduled jobs", once per second by default), so a console `debug` would spam.
6
+ * Silencing it here keeps the default quiet while still surfacing real problems.
7
+ * Inject your own {@link JobLogger} (e.g. the app logger) to capture debug.
8
+ */
9
+ export const defaultJobLogger = {
10
+ error: (message, ...meta) => console.error(message, ...meta),
11
+ warn: (message, ...meta) => console.warn(message, ...meta),
12
+ info: (message, ...meta) => console.info(message, ...meta),
13
+ debug: () => { },
14
+ };
@@ -0,0 +1,19 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Connection } from 'plainjob';
3
+ /**
4
+ * A plainjob {@link Connection} over a `bun:sqlite` Database, with two deliberate
5
+ * deviations from plainjob's own `bun()` adapter:
6
+ *
7
+ * 1. **Named-parameter binding fix.** plainjob binds its prepared statements with
8
+ * *bare* object keys (`{ type, cronExpression }`). On Bun ≥ 1.3 `bun:sqlite`
9
+ * requires the sigil — bare keys silently bind NULL (→ `NOT NULL constraint
10
+ * failed`). This adapter rewrites each object key to `@key` (leaving keys that
11
+ * already carry an `@ $ :` sigil, and non-object/positional params, untouched),
12
+ * so plainjob runs unmodified on current Bun. Remove once plainjob ships a fix.
13
+ *
14
+ * 2. **`close()` is a no-op.** plainjob's queue and the app share ONE connection;
15
+ * `queue.close()` would otherwise close the app's database out from under it.
16
+ * Closing is the app's job — the runner only stops the queue's maintenance
17
+ * timer (which `queue.close()` still does). The DB outlives the runner.
18
+ */
19
+ export declare function createPlainjobConnection(database: Database): Connection;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * A plainjob {@link Connection} over a `bun:sqlite` Database, with two deliberate
3
+ * deviations from plainjob's own `bun()` adapter:
4
+ *
5
+ * 1. **Named-parameter binding fix.** plainjob binds its prepared statements with
6
+ * *bare* object keys (`{ type, cronExpression }`). On Bun ≥ 1.3 `bun:sqlite`
7
+ * requires the sigil — bare keys silently bind NULL (→ `NOT NULL constraint
8
+ * failed`). This adapter rewrites each object key to `@key` (leaving keys that
9
+ * already carry an `@ $ :` sigil, and non-object/positional params, untouched),
10
+ * so plainjob runs unmodified on current Bun. Remove once plainjob ships a fix.
11
+ *
12
+ * 2. **`close()` is a no-op.** plainjob's queue and the app share ONE connection;
13
+ * `queue.close()` would otherwise close the app's database out from under it.
14
+ * Closing is the app's job — the runner only stops the queue's maintenance
15
+ * timer (which `queue.close()` still does). The DB outlives the runner.
16
+ */
17
+ export function createPlainjobConnection(database) {
18
+ const prefixKeys = (params) => params.map((param) => {
19
+ if (!param || typeof param !== 'object' || Array.isArray(param))
20
+ return param;
21
+ return Object.fromEntries(Object.entries(param).map(([key, value]) => [
22
+ /^[@$:]/.test(key) ? key : `@${key}`,
23
+ value,
24
+ ]));
25
+ });
26
+ return {
27
+ driver: 'bun:sqlite',
28
+ filename: database.filename,
29
+ pragma: (stmt) => {
30
+ database.exec(`PRAGMA ${stmt};`);
31
+ },
32
+ exec: (stmt) => {
33
+ database.exec(stmt);
34
+ },
35
+ transaction: database.transaction.bind(database),
36
+ prepare: (sql) => {
37
+ const stmt = database.query(sql);
38
+ return {
39
+ run: (...params) => stmt.run(...prefixKeys(params)),
40
+ get: (...params) => stmt.get(...prefixKeys(params)),
41
+ all: (...params) => stmt.all(...prefixKeys(params)),
42
+ };
43
+ },
44
+ close: () => {
45
+ // Intentionally empty — the app owns the shared DB's lifecycle.
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,17 @@
1
+ import type { PollingJob, PollingJobOpts } from './types';
2
+ /**
3
+ * A periodic, restart-aware poller — the engine behind ca's `setInterval`
4
+ * scheduler, alarm check, and metrics flush. On each tick it runs the handler and,
5
+ * on success, upserts `job_state.last_run_at` so the *last successful* time
6
+ * survives a restart (the `runOnBoot` + `last_run_at` pair is how a missed daily
7
+ * job catches up: fire on boot, and the handler skips if it already ran recently).
8
+ *
9
+ * Hardening over a bare `setInterval`:
10
+ * - the timer is `unref()`'d, so a poller never keeps the process alive on its own;
11
+ * - ticks are non-reentrant — a slow run can't pile up overlapping fires;
12
+ * - {@link PollingJob.stop} aborts the in-flight run's context signal;
13
+ * - a thrown handler is logged and does NOT advance `last_run_at`.
14
+ *
15
+ * `job_state` must already exist (the app's migration runner builds it).
16
+ */
17
+ export declare function createPollingJob(opts: PollingJobOpts): PollingJob;
@@ -0,0 +1,79 @@
1
+ import { defaultJobLogger } from './logger';
2
+ /**
3
+ * A periodic, restart-aware poller — the engine behind ca's `setInterval`
4
+ * scheduler, alarm check, and metrics flush. On each tick it runs the handler and,
5
+ * on success, upserts `job_state.last_run_at` so the *last successful* time
6
+ * survives a restart (the `runOnBoot` + `last_run_at` pair is how a missed daily
7
+ * job catches up: fire on boot, and the handler skips if it already ran recently).
8
+ *
9
+ * Hardening over a bare `setInterval`:
10
+ * - the timer is `unref()`'d, so a poller never keeps the process alive on its own;
11
+ * - ticks are non-reentrant — a slow run can't pile up overlapping fires;
12
+ * - {@link PollingJob.stop} aborts the in-flight run's context signal;
13
+ * - a thrown handler is logged and does NOT advance `last_run_at`.
14
+ *
15
+ * `job_state` must already exist (the app's migration runner builds it).
16
+ */
17
+ export function createPollingJob(opts) {
18
+ const logger = opts.logger ?? defaultJobLogger;
19
+ const runOnBoot = opts.runOnBoot !== false;
20
+ // Preserve any existing `meta` JSON on conflict — only last_run_at moves.
21
+ const upsertState = opts.db.prepare(`INSERT INTO job_state (name, last_run_at) VALUES (?, ?)
22
+ ON CONFLICT(name) DO UPDATE SET last_run_at = excluded.last_run_at`);
23
+ let timer = null;
24
+ let controller = null;
25
+ let inFlight = false;
26
+ const fire = async () => {
27
+ if (inFlight || !controller)
28
+ return;
29
+ inFlight = true;
30
+ // Captured, not re-read: `stop()` nulls `controller` and a later `start()`
31
+ // installs a fresh one, so the identity check below is what tells "still the
32
+ // run I began" from "stopped, and possibly restarted, while I was awaiting".
33
+ const mine = controller;
34
+ const context = { db: opts.db, signal: mine.signal, logger };
35
+ try {
36
+ await opts.run(context);
37
+ // 🔴 STOPPED while this run was in flight — do not stamp a completion.
38
+ //
39
+ // Two reasons, and the second is the one that showed up. Semantically, a
40
+ // job whose owner pulled the plug mid-run did not finish on its own terms,
41
+ // so recording `last_run_at` would make the next boot skip work that never
42
+ // happened. Practically, `stop()` is what a host or a test calls just
43
+ // before closing the database — so this statement ran against a closed
44
+ // handle and threw "Database has closed" out of a timer callback, after
45
+ // the test that owned it had ended. That surfaced as an unhandled error
46
+ // reddening cursed-satellites' orch-engine suite roughly one run in three,
47
+ // with a stack pointing here and nothing to do with the test that failed.
48
+ if (controller !== mine)
49
+ return;
50
+ upsertState.run(opts.name, new Date().toISOString());
51
+ }
52
+ catch (err) {
53
+ logger.error(`[polling] ${opts.name} failed`, err);
54
+ }
55
+ finally {
56
+ inFlight = false;
57
+ }
58
+ };
59
+ return {
60
+ name: opts.name,
61
+ start() {
62
+ if (timer)
63
+ return;
64
+ controller = new AbortController();
65
+ if (runOnBoot)
66
+ void fire();
67
+ timer = setInterval(() => void fire(), opts.intervalMs);
68
+ timer.unref?.();
69
+ },
70
+ stop() {
71
+ if (timer) {
72
+ clearInterval(timer);
73
+ timer = null;
74
+ }
75
+ controller?.abort(new Error(`polling job "${opts.name}" stopped`));
76
+ controller = null;
77
+ },
78
+ };
79
+ }
@@ -0,0 +1,24 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { type CreateJobRunnerOptions, type JobRunner } from './types';
3
+ /**
4
+ * Create the durable cron job runner — a thin facade over **plainjob** (MIT,
5
+ * SQLite-backed, no Redis). It generalizes ca's scattered `setInterval` cron-ish
6
+ * pollers + backup-target-hour check into one restart-aware runner whose schedules
7
+ * live in the *same* SQLite file as the business data (no separate store).
8
+ *
9
+ * Audit trail: every run (cron-fired or {@link JobRunner.runOnce}) writes a
10
+ * `job_runs` row — `running` on start, then `ok`/`error` on finish — so the admin
11
+ * "last run" view and {@link JobRunner.getLastRun} reflect reality. plainjob keeps
12
+ * its OWN internal tables (`plainjob_jobs`, `plainjob_scheduled_jobs`); the
13
+ * `job_runs`/`job_state` plumbing tables (task 22) are this wrapper's audit view
14
+ * and never collide with them.
15
+ *
16
+ * History is bounded by default ({@link DEFAULT_JOB_RETENTION}): `job_runs` is
17
+ * pruned lazily (age window + per-job row cap, at most once per prune interval),
18
+ * and plainjob's maintenance timer removes its internal done/failed rows on the
19
+ * configured horizons — so a sub-minute cron can run for months without the DB
20
+ * growing without bound.
21
+ *
22
+ * The `job_runs` table must already exist (the app's migration runner builds it).
23
+ */
24
+ export declare function createJobRunner(db: Database, opts?: CreateJobRunnerOptions): JobRunner;
@@ -0,0 +1,186 @@
1
+ import { defineQueue, defineWorker } from 'plainjob';
2
+ import { ApiError } from '../errors';
3
+ import { defaultJobLogger } from './logger';
4
+ import { createPlainjobConnection } from './plainjobAdapter';
5
+ import { withTimeout } from './timeout';
6
+ import { DEFAULT_JOB_RETENTION, DEFAULT_JOB_TIMEOUT_MS, } from './types';
7
+ /**
8
+ * Create the durable cron job runner — a thin facade over **plainjob** (MIT,
9
+ * SQLite-backed, no Redis). It generalizes ca's scattered `setInterval` cron-ish
10
+ * pollers + backup-target-hour check into one restart-aware runner whose schedules
11
+ * live in the *same* SQLite file as the business data (no separate store).
12
+ *
13
+ * Audit trail: every run (cron-fired or {@link JobRunner.runOnce}) writes a
14
+ * `job_runs` row — `running` on start, then `ok`/`error` on finish — so the admin
15
+ * "last run" view and {@link JobRunner.getLastRun} reflect reality. plainjob keeps
16
+ * its OWN internal tables (`plainjob_jobs`, `plainjob_scheduled_jobs`); the
17
+ * `job_runs`/`job_state` plumbing tables (task 22) are this wrapper's audit view
18
+ * and never collide with them.
19
+ *
20
+ * History is bounded by default ({@link DEFAULT_JOB_RETENTION}): `job_runs` is
21
+ * pruned lazily (age window + per-job row cap, at most once per prune interval),
22
+ * and plainjob's maintenance timer removes its internal done/failed rows on the
23
+ * configured horizons — so a sub-minute cron can run for months without the DB
24
+ * growing without bound.
25
+ *
26
+ * The `job_runs` table must already exist (the app's migration runner builds it).
27
+ */
28
+ export function createJobRunner(db, opts = {}) {
29
+ const logger = opts.logger ?? defaultJobLogger;
30
+ const pollIntervalMs = opts.pollIntervalMs ?? 1000;
31
+ const retention = { ...DEFAULT_JOB_RETENTION, ...opts.retention };
32
+ const defs = new Map();
33
+ const workers = new Map();
34
+ let queue = null;
35
+ let started = false;
36
+ let stopped = false;
37
+ // Aborted on stop() so every in-flight handler's context signal fires.
38
+ const stopController = new AbortController();
39
+ const insertRun = db.prepare("INSERT INTO job_runs (id, job_name, started_at, status) VALUES (?, ?, ?, 'running')");
40
+ const finishRun = db.prepare('UPDATE job_runs SET ended_at = ?, status = ?, error = ? WHERE id = ?');
41
+ const lastRunQuery = db.query(`SELECT started_at AS startedAt, ended_at AS endedAt, status, error
42
+ FROM job_runs WHERE job_name = ? ORDER BY started_at DESC, rowid DESC LIMIT 1`);
43
+ let lastPruneAt = 0;
44
+ /**
45
+ * Lazily bound the `job_runs` audit table: an age window (ISO-8601 strings
46
+ * compare lexicographically, so plain `<` is correct) plus a newest-N cap per
47
+ * job name. Runs at most once per `pruneIntervalMs`, after a run finishes, and
48
+ * never lets a prune failure disturb the run that triggered it.
49
+ */
50
+ function maybePruneAudit() {
51
+ const now = Date.now();
52
+ if (now - lastPruneAt < retention.pruneIntervalMs)
53
+ return;
54
+ lastPruneAt = now;
55
+ try {
56
+ if (retention.auditMaxAgeMs > 0) {
57
+ db.query('DELETE FROM job_runs WHERE started_at < ?').run(new Date(now - retention.auditMaxAgeMs).toISOString());
58
+ }
59
+ if (retention.auditMaxRowsPerJob > 0) {
60
+ db.query(`DELETE FROM job_runs WHERE id IN (
61
+ SELECT id FROM (
62
+ SELECT id, ROW_NUMBER() OVER (
63
+ PARTITION BY job_name ORDER BY started_at DESC, rowid DESC
64
+ ) AS rn FROM job_runs
65
+ ) WHERE rn > ?
66
+ )`).run(retention.auditMaxRowsPerJob);
67
+ }
68
+ }
69
+ catch (err) {
70
+ logger.warn('[jobs] audit prune failed (non-fatal)', err);
71
+ }
72
+ }
73
+ // plainjob's queue (and its maintenance timer) is created lazily — a runner with
74
+ // only manual (`runOnce`) jobs never touches plainjob at all. A retention field
75
+ // of 0 means "never remove"; plainjob has no explicit off switch, so it maps to
76
+ // an effectively-infinite horizon.
77
+ function ensureQueue() {
78
+ if (!queue) {
79
+ queue = defineQueue({
80
+ connection: createPlainjobConnection(db),
81
+ logger,
82
+ removeDoneJobsOlderThan: retention.doneJobsMaxAgeMs > 0 ? retention.doneJobsMaxAgeMs : Number.MAX_SAFE_INTEGER,
83
+ removeFailedJobsOlderThan: retention.failedJobsMaxAgeMs > 0 ? retention.failedJobsMaxAgeMs : Number.MAX_SAFE_INTEGER,
84
+ });
85
+ }
86
+ return queue;
87
+ }
88
+ /**
89
+ * Run one job: write the audit row, build a per-run abort signal (fires on stop
90
+ * OR timeout), run with the timeout, then record `ok`/`error`. Re-throws only
91
+ * when `rethrow` is set — the cron worker path swallows (the audit row is the
92
+ * record of truth, and plainjob would otherwise double-log + retain the failure),
93
+ * while `runOnce` surfaces the error to its admin caller.
94
+ */
95
+ async function executeJob(def, rethrow) {
96
+ const id = crypto.randomUUID();
97
+ insertRun.run(id, def.name, new Date().toISOString());
98
+ const runController = new AbortController();
99
+ const propagateStop = () => runController.abort(stopController.signal.reason);
100
+ if (stopController.signal.aborted)
101
+ propagateStop();
102
+ else
103
+ stopController.signal.addEventListener('abort', propagateStop, { once: true });
104
+ const context = { db, signal: runController.signal, logger };
105
+ try {
106
+ await withTimeout(Promise.resolve(def.run(context)), def.timeout ?? DEFAULT_JOB_TIMEOUT_MS, () => runController.abort(new Error(`job "${def.name}" timed out`)));
107
+ finishRun.run(new Date().toISOString(), 'ok', null, id);
108
+ }
109
+ catch (err) {
110
+ finishRun.run(new Date().toISOString(), 'error', String(err), id);
111
+ logger.error(`[jobs] ${def.name} failed`, err);
112
+ if (rethrow)
113
+ throw err;
114
+ }
115
+ finally {
116
+ stopController.signal.removeEventListener('abort', propagateStop);
117
+ maybePruneAudit();
118
+ }
119
+ }
120
+ function startWorker(def) {
121
+ const worker = defineWorker(def.name, () => executeJob(def, false), {
122
+ queue: ensureQueue(),
123
+ pollIntervall: pollIntervalMs,
124
+ logger,
125
+ });
126
+ workers.set(def.name, worker);
127
+ // start() resolves only when the worker stops; run it detached.
128
+ void worker.start().catch((err) => logger.error(`[jobs] worker "${def.name}" crashed`, err));
129
+ }
130
+ return {
131
+ register(job) {
132
+ if (stopped)
133
+ throw new Error('cannot register on a stopped job runner');
134
+ if (defs.has(job.name))
135
+ throw new Error(`job already registered: ${job.name}`);
136
+ defs.set(job.name, job);
137
+ if (job.cron) {
138
+ // schedule() upserts by type, so re-registering across restarts just
139
+ // refreshes the cron expression (no duplicate scheduled rows).
140
+ ensureQueue().schedule(job.name, { cron: job.cron });
141
+ if (started)
142
+ startWorker(job);
143
+ }
144
+ },
145
+ start() {
146
+ if (stopped)
147
+ throw new Error('cannot restart a stopped job runner');
148
+ if (started)
149
+ return;
150
+ started = true;
151
+ for (const def of defs.values())
152
+ if (def.cron)
153
+ startWorker(def);
154
+ },
155
+ async stop() {
156
+ if (stopped)
157
+ return;
158
+ stopped = true;
159
+ started = false;
160
+ stopController.abort(new Error('job runner stopped'));
161
+ await Promise.all([...workers.values()].map((worker) => worker.stop()));
162
+ workers.clear();
163
+ // Closes the queue's maintenance timer; the adapter's no-op close keeps the
164
+ // shared DB open for the rest of shutdown.
165
+ queue?.close();
166
+ queue = null;
167
+ },
168
+ async runOnce(name) {
169
+ const def = defs.get(name);
170
+ if (!def)
171
+ throw new ApiError(404, `Unknown job: ${name}`);
172
+ await executeJob(def, true);
173
+ },
174
+ getLastRun(name) {
175
+ const row = lastRunQuery.get(name);
176
+ if (!row)
177
+ return null;
178
+ const record = { startedAt: row.startedAt, status: row.status };
179
+ if (row.endedAt !== null)
180
+ record.endedAt = row.endedAt;
181
+ if (row.error !== null)
182
+ record.error = row.error;
183
+ return record;
184
+ },
185
+ };
186
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A promise-timeout for in-process job handlers.
3
+ *
4
+ * NB: cwip's `runWithTimeout` is a *subprocess* runner (it spawns + kills a child
5
+ * process), so it is the wrong tool for racing an in-process handler promise — we
6
+ * own this tiny helper instead. The timer is `unref()`'d so it never keeps the
7
+ * event loop alive, and it is always cleared once the race settles.
8
+ */
9
+ /** Thrown when a job handler exceeds its `timeout`. */
10
+ export declare class JobTimeoutError extends Error {
11
+ readonly timeoutMs: number;
12
+ constructor(timeoutMs: number);
13
+ }
14
+ /**
15
+ * Resolve/reject with `promise`, but reject with a {@link JobTimeoutError} if it
16
+ * has not settled within `timeoutMs`. `onTimeout` fires on timeout — the runner
17
+ * uses it to abort the handler's context signal so a cooperative handler can
18
+ * short-circuit (a pending promise cannot be force-cancelled).
19
+ *
20
+ * Order matters: the timeout promise is rejected BEFORE `onTimeout` runs. Aborting
21
+ * the signal can synchronously resolve a cooperative handler; if that resolution
22
+ * reached `Promise.race` first the run would be mis-recorded as `ok`. Rejecting
23
+ * first guarantees a timed-out run is recorded as a timeout, not a success.
24
+ *
25
+ * A non-positive / non-finite `timeoutMs` means "no timeout": `promise` is
26
+ * returned unchanged.
27
+ */
28
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout?: () => void): Promise<T>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A promise-timeout for in-process job handlers.
3
+ *
4
+ * NB: cwip's `runWithTimeout` is a *subprocess* runner (it spawns + kills a child
5
+ * process), so it is the wrong tool for racing an in-process handler promise — we
6
+ * own this tiny helper instead. The timer is `unref()`'d so it never keeps the
7
+ * event loop alive, and it is always cleared once the race settles.
8
+ */
9
+ /** Thrown when a job handler exceeds its `timeout`. */
10
+ export class JobTimeoutError extends Error {
11
+ timeoutMs;
12
+ constructor(timeoutMs) {
13
+ super(`Job timed out after ${timeoutMs}ms`);
14
+ this.name = 'JobTimeoutError';
15
+ this.timeoutMs = timeoutMs;
16
+ }
17
+ }
18
+ /**
19
+ * Resolve/reject with `promise`, but reject with a {@link JobTimeoutError} if it
20
+ * has not settled within `timeoutMs`. `onTimeout` fires on timeout — the runner
21
+ * uses it to abort the handler's context signal so a cooperative handler can
22
+ * short-circuit (a pending promise cannot be force-cancelled).
23
+ *
24
+ * Order matters: the timeout promise is rejected BEFORE `onTimeout` runs. Aborting
25
+ * the signal can synchronously resolve a cooperative handler; if that resolution
26
+ * reached `Promise.race` first the run would be mis-recorded as `ok`. Rejecting
27
+ * first guarantees a timed-out run is recorded as a timeout, not a success.
28
+ *
29
+ * A non-positive / non-finite `timeoutMs` means "no timeout": `promise` is
30
+ * returned unchanged.
31
+ */
32
+ export function withTimeout(promise, timeoutMs, onTimeout) {
33
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
34
+ return promise;
35
+ let timer;
36
+ const timeout = new Promise((_resolve, reject) => {
37
+ timer = setTimeout(() => {
38
+ reject(new JobTimeoutError(timeoutMs));
39
+ onTimeout?.();
40
+ }, timeoutMs);
41
+ timer.unref?.();
42
+ });
43
+ return Promise.race([promise, timeout]).finally(() => {
44
+ if (timer)
45
+ clearTimeout(timer);
46
+ });
47
+ }