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,69 @@
1
+ import { ABBREV_TO_METHOD, METHOD_TO_ABBREV, } from 'cursedbelt-core/ctgr/types';
2
+ import { V0ChunkStore } from 'cursedbelt-core/ctgr/v0compat';
3
+ import { ApiError, toErrorEnvelope } from './errors';
4
+ /**
5
+ * The path pattern that matches EVERY ctgr GET twin (`/ctgr<XX>...`) and nothing
6
+ * else — mount the decode + CSRF + getOnly middleware on this. NOTE: Hono's router
7
+ * does not treat `/ctgr*` as a prefix wildcard (a bare-suffix `*` only matches a
8
+ * single following segment), so a named param with a `ctgr.+` regex is the correct,
9
+ * portable matcher (`.` spans `/`, so it covers multi-segment paths like
10
+ * `/ctgrpa/api/v1/lists/123`). Reuse this everywhere a `/ctgr*` mount is meant.
11
+ */
12
+ export const CTGR_ROUTE_PATTERN = '/:ctgrTwin{ctgr.+}';
13
+ /**
14
+ * Mount on {@link CTGR_ROUTE_PATTERN} (before the twin routes). A single chunk decodes
15
+ * inline; a multi-chunk payload accumulates in the injected {@link ChunkStore}
16
+ * (intermediate chunk → 202). The decoded request + method are stashed on the context for the
17
+ * downstream handler / validator (`c.get('ctgrDecoded')`). Legacy `_cv`-absent
18
+ * requests fall back to the v0 decoder for the rollout window.
19
+ */
20
+ export function unGETify(store) {
21
+ const legacy = new V0ChunkStore();
22
+ return async (c, next) => {
23
+ const params = Object.fromEntries(new URL(c.req.url).searchParams);
24
+ const pathMethod = ABBREV_TO_METHOD[c.req.path.slice(5, 7)];
25
+ if (!pathMethod) {
26
+ return c.json(toErrorEnvelope(new ApiError(400, 'ctgr: unknown method twin', { code: 'unknown_method' })), 400);
27
+ }
28
+ const result = params._cv === undefined ? legacy.add(params, pathMethod) : await store.add(params);
29
+ if (result === false)
30
+ return c.json({ acknowledged: true }, 202);
31
+ if (result instanceof Error)
32
+ return c.json(toErrorEnvelope(new ApiError(400, result.message, { code: 'ctgr_decode_failed' })), 400);
33
+ c.set('ctgrDecoded', result);
34
+ c.set('ctgrMethod', result.method);
35
+ await next();
36
+ };
37
+ }
38
+ /** Read the ctgr-decoded request a `/ctgr*` twin stashed, if this was a tunneled call. */
39
+ export function getCtgrDecoded(c) {
40
+ return c.get('ctgrDecoded');
41
+ }
42
+ /**
43
+ * Twin registrar: one call registers the real verb route AND its `/ctgr<XX>` GET twin
44
+ * (same handlers; the global `/ctgr*` {@link unGETify} middleware decodes the body
45
+ * first). Binary/download routes MUST NOT go through this — register those as a plain
46
+ * `app.get(path, handler)` so their bytes never ride the base64url tunnel and get
47
+ * mangled.
48
+ */
49
+ export function buildRoutes(app) {
50
+ const on = app.on.bind(app);
51
+ return function register(method, path,
52
+ /*
53
+ * `H`, not `MiddlewareHandler`. These are ROUTE handlers: Hono's
54
+ * `MiddlewareHandler` must return `Promise<Response | void>`, so declaring it
55
+ * here forced every caller to be `async` for no reason — a plain
56
+ * `(c) => c.json(…)` did not typecheck against a registrar whose whole job is
57
+ * registering exactly that. `H` is Hono's own union of both kinds and is what
58
+ * `app.on` accepts.
59
+ */
60
+ ...handlers) {
61
+ const upper = method.toUpperCase();
62
+ const abbrev = METHOD_TO_ABBREV[upper];
63
+ if (!abbrev)
64
+ throw new Error(`buildRoutes: "${method}" is not a ctgr-tunnelable method`);
65
+ // The real verb route + its GET twin, from one call.
66
+ on(upper, path, ...handlers);
67
+ on('GET', `/ctgr${abbrev}${path}`, ...handlers);
68
+ };
69
+ }
@@ -0,0 +1,14 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { Kysely } from 'kysely';
3
+ import type { PlumbingSchema } from './plumbingTypes';
4
+ /**
5
+ * A Kysely query builder typed to the {@link PlumbingSchema} plumbing tables, on the
6
+ * `bun:sqlite` dialect. Use it for type-checked reads/writes of sessions, config,
7
+ * logs, metrics, and jobs. Business / `data JSON` tables stay on raw `db.query()` —
8
+ * do NOT add them here (that would couple the Kysely schema to every feature table).
9
+ *
10
+ * The dialect wraps the SAME `Database` handle the rest of the app uses, so there is
11
+ * one connection, one WAL, one transaction space.
12
+ */
13
+ export declare function createKysely(db: Database): Kysely<PlumbingSchema>;
14
+ export type PlumbingDb = Kysely<PlumbingSchema>;
@@ -0,0 +1,16 @@
1
+ import { Kysely } from 'kysely';
2
+ import { BunSqliteDialect } from 'kysely-bun-sqlite';
3
+ /**
4
+ * A Kysely query builder typed to the {@link PlumbingSchema} plumbing tables, on the
5
+ * `bun:sqlite` dialect. Use it for type-checked reads/writes of sessions, config,
6
+ * logs, metrics, and jobs. Business / `data JSON` tables stay on raw `db.query()` —
7
+ * do NOT add them here (that would couple the Kysely schema to every feature table).
8
+ *
9
+ * The dialect wraps the SAME `Database` handle the rest of the app uses, so there is
10
+ * one connection, one WAL, one transaction space.
11
+ */
12
+ export function createKysely(db) {
13
+ return new Kysely({
14
+ dialect: new BunSqliteDialect({ database: db }),
15
+ });
16
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * TypeScript row interfaces for the *plumbing* tables — the structural, non-business
3
+ * tables cursedbelt owns: sessions, config, logs, metrics, jobs, the migration
4
+ * sentinel. These get Kysely-typed access (see `./kysely`) so the toolkit's own
5
+ * queries are type-checked.
6
+ *
7
+ * Business / feature tables (notes, lists, shares, …) deliberately stay OUT of this
8
+ * schema: they keep raw `db.query()` + flexible `data JSON`, so the Kysely schema is
9
+ * not coupled to every feature table. Shapes mirror the canonical DDL (task 22,
10
+ * `0001_initial`). INTEGER booleans are kept as `number` (SQLite has no real bool).
11
+ */
12
+ export interface AuthSessionRow {
13
+ session_id: string;
14
+ user_id: string;
15
+ created_at: string;
16
+ last_seen_at: string;
17
+ refresh_expires_at: string;
18
+ access_expires_at: string | null;
19
+ /** 0 | 1 — the `/f` GET-only restriction, enforced + audited per session. */
20
+ get_only: number;
21
+ /** 0 | 1 */
22
+ disabled: number;
23
+ disabled_at: string | null;
24
+ disabled_by: string | null;
25
+ user_agent: string | null;
26
+ ip: string | null;
27
+ }
28
+ export interface AppConfigRow {
29
+ key: string;
30
+ value: string;
31
+ updated_at: string;
32
+ updated_by: string | null;
33
+ }
34
+ export interface EventLogRow {
35
+ id: string;
36
+ correlation_id: string | null;
37
+ event_name: string;
38
+ /** info | warn | error (free text, defaults to 'info'). */
39
+ type: string;
40
+ message: string | null;
41
+ /** JSON blob of structured detail. */
42
+ details: string | null;
43
+ duration_ms: number | null;
44
+ user_id: string | null;
45
+ created_at: string;
46
+ }
47
+ export interface RequestMetricRow {
48
+ id: string;
49
+ /** Epoch ms. */
50
+ ts: number;
51
+ method: string | null;
52
+ route: string | null;
53
+ status: number | null;
54
+ duration_ms: number | null;
55
+ bytes_out: number | null;
56
+ user_id: string | null;
57
+ }
58
+ export interface SystemSampleRow {
59
+ id: string;
60
+ /** Epoch ms. */
61
+ ts: number;
62
+ rss: number | null;
63
+ heap_used: number | null;
64
+ heap_total: number | null;
65
+ os_used: number | null;
66
+ os_total: number | null;
67
+ uptime_s: number | null;
68
+ event_loop_lag_ms: number | null;
69
+ ws_clients: number | null;
70
+ req_count: number | null;
71
+ err_count: number | null;
72
+ avg_duration_ms: number | null;
73
+ }
74
+ export interface EmailFailureRow {
75
+ id: string;
76
+ created_at: string;
77
+ recipient: string;
78
+ subject: string;
79
+ /** Defaults to 'send_error'. */
80
+ reason: string;
81
+ error_name: string | null;
82
+ error_code: string | null;
83
+ error_message: string | null;
84
+ details: string | null;
85
+ }
86
+ export interface JobStateRow {
87
+ name: string;
88
+ last_run_at: string | null;
89
+ /** JSON blob. */
90
+ meta: string | null;
91
+ }
92
+ export interface JobRunRow {
93
+ id: string;
94
+ job_name: string;
95
+ started_at: string;
96
+ ended_at: string | null;
97
+ /** running | ok | error (defaults to 'running'). */
98
+ status: string;
99
+ error: string | null;
100
+ }
101
+ export interface MigrationRow {
102
+ name: string;
103
+ applied_at: string;
104
+ }
105
+ /** The Kysely database schema for cursedbelt plumbing tables. */
106
+ export interface PlumbingSchema {
107
+ auth_sessions: AuthSessionRow;
108
+ app_config: AppConfigRow;
109
+ event_logs: EventLogRow;
110
+ request_metrics: RequestMetricRow;
111
+ system_samples: SystemSampleRow;
112
+ email_failures: EmailFailureRow;
113
+ job_state: JobStateRow;
114
+ job_runs: JobRunRow;
115
+ _migrations: MigrationRow;
116
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * TypeScript row interfaces for the *plumbing* tables — the structural, non-business
3
+ * tables cursedbelt owns: sessions, config, logs, metrics, jobs, the migration
4
+ * sentinel. These get Kysely-typed access (see `./kysely`) so the toolkit's own
5
+ * queries are type-checked.
6
+ *
7
+ * Business / feature tables (notes, lists, shares, …) deliberately stay OUT of this
8
+ * schema: they keep raw `db.query()` + flexible `data JSON`, so the Kysely schema is
9
+ * not coupled to every feature table. Shapes mirror the canonical DDL (task 22,
10
+ * `0001_initial`). INTEGER booleans are kept as `number` (SQLite has no real bool).
11
+ */
12
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Asking binary-server "do you already have this?" — the client half.
3
+ *
4
+ * Owner's ask, 2026-08-13: *"make strategy to always check for duplicate binaries in same app
5
+ * for same user, automatically skip if it's literally a duplicate file"* … *"if it's an
6
+ * automated upload with ingestions or an agent guiding it and I need to make a decision put it
7
+ * in binary-server UI for me to see and handle it."*
8
+ *
9
+ * ── 🔴 Asked BEFORE the bytes move, and that ordering is the whole point ───────────────
10
+ * The obvious design is upload-then-detect: store it, notice the collision, delete one. That
11
+ * moves every duplicate twice, and on a media dropzone it does so for multi-gigabyte video the
12
+ * store already holds. The scraper has the file, so it has the digest; one small POST answers
13
+ * the question before a single byte is sent.
14
+ *
15
+ * ── 🔴 It lives in cursedbelt, not in satellite-kit ────────────────────────────────────
16
+ * binary-server itself consumes `cursedbelt` and does NOT consume `satellite-kit`, and every
17
+ * ingesting app consumes both. This is the one shelf both sides can see. It is also the reason
18
+ * the request/response shape is stated here as types rather than being re-declared by each
19
+ * caller — a dropzone that got `hold` backwards would upload exactly the files the owner asked
20
+ * it to park.
21
+ */
22
+ export interface DuplicateQuestion {
23
+ /** binary-server's loopback origin. Never the public tunnel — see the note below. */
24
+ baseUrl: string;
25
+ /** The shared internal secret; this route is internal-only and 403s without it. */
26
+ internalSecret: string;
27
+ /** The binary-server tenant, e.g. `music`. */
28
+ app: string;
29
+ /** Scope the comparison to one user. Null on the owner-only apps, which is normal. */
30
+ ownerId?: string | null;
31
+ /** sha256 of the bytes about to be uploaded, hex. {@link hashFile} produces it. */
32
+ checksum: string;
33
+ size: number;
34
+ /** The name the object would carry — what the "same name" half of the rule compares. */
35
+ name: string;
36
+ /** Where the file is right now, so the owner's later decision can act on it. */
37
+ heldPath?: string | null;
38
+ }
39
+ export interface DuplicateVerdict {
40
+ /** Present when the store already holds these exact bytes. Skip the upload entirely. */
41
+ exact: {
42
+ key: string;
43
+ title: string | null;
44
+ size: number;
45
+ } | null;
46
+ /** Name-alikes with different bytes — a question, never an instruction. */
47
+ likely: {
48
+ key: string;
49
+ title: string | null;
50
+ size: number;
51
+ reason: string;
52
+ }[];
53
+ /** True when the caller must NOT upload and must park the file for the owner. */
54
+ hold: boolean;
55
+ /** The review-queue row binary-server filed, when it filed one. */
56
+ candidateId: string | null;
57
+ }
58
+ /**
59
+ * Streaming sha256 of a file on disk.
60
+ *
61
+ * Streamed rather than buffered because a music library is thousands of files and some of them
62
+ * are 60 MB; reading each one whole to hash it is how an ingest becomes a memory problem.
63
+ */
64
+ export declare function hashFile(path: string): Promise<string>;
65
+ /**
66
+ * Ask binary-server about one incoming file.
67
+ *
68
+ * 🔴 **Fails OPEN, deliberately.** If binary-server cannot be reached, or answers something
69
+ * unexpected, the verdict is "no duplicate, do not hold" and the ingest proceeds. The
70
+ * alternative is that a dropzone stops taking the owner's files because a de-duplication
71
+ * nicety is down — trading a real capability for a housekeeping one. A duplicate that slips
72
+ * through costs disk and is found later by the scan button, which is exactly what that button
73
+ * is for.
74
+ *
75
+ * 🔴 **`baseUrl` must be the LOOPBACK origin.** The route is absent from binary-server's
76
+ * public path allowlist and 404s when the request arrives bearing the public tunnel Host —
77
+ * because it reports the titles and sizes of stored objects, and listing is what the tunnel
78
+ * must never publish. A caller that passes the public URL gets a 404 and, through the
79
+ * fail-open above, silently loses de-duplication entirely. notes shipped this exact bug once
80
+ * with `/api/transcode`.
81
+ */
82
+ export declare function askBinaryServerAboutDuplicate(q: DuplicateQuestion): Promise<DuplicateVerdict>;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Asking binary-server "do you already have this?" — the client half.
3
+ *
4
+ * Owner's ask, 2026-08-13: *"make strategy to always check for duplicate binaries in same app
5
+ * for same user, automatically skip if it's literally a duplicate file"* … *"if it's an
6
+ * automated upload with ingestions or an agent guiding it and I need to make a decision put it
7
+ * in binary-server UI for me to see and handle it."*
8
+ *
9
+ * ── 🔴 Asked BEFORE the bytes move, and that ordering is the whole point ───────────────
10
+ * The obvious design is upload-then-detect: store it, notice the collision, delete one. That
11
+ * moves every duplicate twice, and on a media dropzone it does so for multi-gigabyte video the
12
+ * store already holds. The scraper has the file, so it has the digest; one small POST answers
13
+ * the question before a single byte is sent.
14
+ *
15
+ * ── 🔴 It lives in cursedbelt, not in satellite-kit ────────────────────────────────────
16
+ * binary-server itself consumes `cursedbelt` and does NOT consume `satellite-kit`, and every
17
+ * ingesting app consumes both. This is the one shelf both sides can see. It is also the reason
18
+ * the request/response shape is stated here as types rather than being re-declared by each
19
+ * caller — a dropzone that got `hold` backwards would upload exactly the files the owner asked
20
+ * it to park.
21
+ */
22
+ import { createHash } from "node:crypto";
23
+ import { createReadStream } from "node:fs";
24
+ /**
25
+ * Streaming sha256 of a file on disk.
26
+ *
27
+ * Streamed rather than buffered because a music library is thousands of files and some of them
28
+ * are 60 MB; reading each one whole to hash it is how an ingest becomes a memory problem.
29
+ */
30
+ export function hashFile(path) {
31
+ return new Promise((resolve, reject) => {
32
+ const hash = createHash("sha256");
33
+ const stream = createReadStream(path);
34
+ stream.on("error", reject);
35
+ stream.on("data", (chunk) => hash.update(chunk));
36
+ stream.on("end", () => resolve(hash.digest("hex")));
37
+ });
38
+ }
39
+ /**
40
+ * Ask binary-server about one incoming file.
41
+ *
42
+ * 🔴 **Fails OPEN, deliberately.** If binary-server cannot be reached, or answers something
43
+ * unexpected, the verdict is "no duplicate, do not hold" and the ingest proceeds. The
44
+ * alternative is that a dropzone stops taking the owner's files because a de-duplication
45
+ * nicety is down — trading a real capability for a housekeeping one. A duplicate that slips
46
+ * through costs disk and is found later by the scan button, which is exactly what that button
47
+ * is for.
48
+ *
49
+ * 🔴 **`baseUrl` must be the LOOPBACK origin.** The route is absent from binary-server's
50
+ * public path allowlist and 404s when the request arrives bearing the public tunnel Host —
51
+ * because it reports the titles and sizes of stored objects, and listing is what the tunnel
52
+ * must never publish. A caller that passes the public URL gets a 404 and, through the
53
+ * fail-open above, silently loses de-duplication entirely. notes shipped this exact bug once
54
+ * with `/api/transcode`.
55
+ */
56
+ export async function askBinaryServerAboutDuplicate(q) {
57
+ const none = { exact: null, likely: [], hold: false, candidateId: null };
58
+ if (!q.internalSecret)
59
+ return none;
60
+ try {
61
+ const res = await fetch(`${q.baseUrl.replace(/\/+$/, "")}/internal/ops/duplicates/check`, {
62
+ method: "POST",
63
+ headers: {
64
+ "content-type": "application/json",
65
+ "x-internal-secret": q.internalSecret,
66
+ },
67
+ body: JSON.stringify({
68
+ app: q.app,
69
+ ownerId: q.ownerId ?? null,
70
+ checksum: q.checksum,
71
+ size: q.size,
72
+ name: q.name,
73
+ origin: "automated",
74
+ heldPath: q.heldPath ?? null,
75
+ }),
76
+ });
77
+ if (!res.ok)
78
+ return none;
79
+ const body = (await res.json());
80
+ return {
81
+ exact: body.exact ?? null,
82
+ likely: body.likely ?? [],
83
+ hold: body.hold === true,
84
+ candidateId: body.candidateId ?? null,
85
+ };
86
+ }
87
+ catch {
88
+ return none;
89
+ }
90
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `cursedbelt-server/dropzone` — the reusable drop-folder scraper.
3
+ *
4
+ * Drop a tree of files into a folder; a sweep walks it, hands each file to the app's handler,
5
+ * deletes only what the handler confirmed was filed somewhere else, and prunes the folders it
6
+ * empties. Nothing app-specific lives here: what a file MEANS is the app's, what happens to a
7
+ * file on disk is this module's.
8
+ *
9
+ * Consumers (2026-08-13): `apps/music`, `apps/family`, `apps/collections` in
10
+ * cursed-satellites. Each supplies a `DropHandler` and nothing else.
11
+ */
12
+ export { describeSweep, isJunkName, isStable, pruneEmptyDirs, scanDropzone, sweepDropzone, type DropHandler, type DropOutcome, type DroppedFile, type ScanOptions, type SweepReport, } from "./scan";
13
+ export { askBinaryServerAboutDuplicate, hashFile, type DuplicateVerdict, type DuplicateQuestion, } from "./duplicates";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `cursedbelt-server/dropzone` — the reusable drop-folder scraper.
3
+ *
4
+ * Drop a tree of files into a folder; a sweep walks it, hands each file to the app's handler,
5
+ * deletes only what the handler confirmed was filed somewhere else, and prunes the folders it
6
+ * empties. Nothing app-specific lives here: what a file MEANS is the app's, what happens to a
7
+ * file on disk is this module's.
8
+ *
9
+ * Consumers (2026-08-13): `apps/music`, `apps/family`, `apps/collections` in
10
+ * cursed-satellites. Each supplies a `DropHandler` and nothing else.
11
+ */
12
+ export { describeSweep, isJunkName, isStable, pruneEmptyDirs, scanDropzone, sweepDropzone, } from "./scan";
13
+ export { askBinaryServerAboutDuplicate, hashFile, } from "./duplicates";
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The drop-folder scraper — the reusable half.
3
+ *
4
+ * Owner's ask, 2026-08-13: *"Do this to prepare a reusable strategy for multiple apps to get
5
+ * intelligent processing for binary-server. Some functionality may be reusable in multiple
6
+ * apps in different ways and can go in cb where standardized app functionality is shared. The
7
+ * specific ingestion logic that only binary-server or one app needs can go to the specific
8
+ * app."* … *"Make this tool reusable in case we need something like it on other folders
9
+ * later."*
10
+ *
11
+ * So: WALKING a drop folder, deciding a file has finished copying, sorting content from junk,
12
+ * deleting only what has been confirmed elsewhere, and leaving nothing behind — all of that is
13
+ * the same for music, family photographs, collections videos and notes documents, and all of
14
+ * it is here. What a file MEANS — which album, which artist, which collection — is the app's,
15
+ * and none of it is here.
16
+ *
17
+ * ── 🔴 Three rules that look like paranoia and are not ─────────────────────────────────
18
+ *
19
+ * 1. **A file is deleted only after the destination CONFIRMS it.** The dropzone is the one
20
+ * place the owner's copy may be the only copy: they dragged it in expecting it to be taken.
21
+ * A sweep that deletes on "the upload didn't throw" turns any catalog failure into data
22
+ * loss. The handler returns `stored`/`duplicate` and only then does the original go.
23
+ *
24
+ * 2. **A file must measure the same size twice before it is touched.** Dropping a 4 GB video
25
+ * into a folder is not atomic. Without this the sweep ingests a truncated file, reports
26
+ * success, and deletes the real one as it is still being written. notes' ingester and
27
+ * collections' both learned this the same way.
28
+ *
29
+ * 3. **One bad file never stops the sweep.** Every handler outcome is per-file, and a throw is
30
+ * caught and recorded. The owner's instruction is explicit about the duplicate case — *"This
31
+ * should not stall uploading the rest of the in progress uploads"* — and it generalizes: a
32
+ * dropzone with one unreadable file in it must still drain the other 4,000.
33
+ */
34
+ /** A file the sweep found, with everything a handler needs to decide about it. */
35
+ export interface DroppedFile {
36
+ /** Absolute path on disk. */
37
+ path: string;
38
+ /** Path relative to the dropzone root — the app's only clue about where it was filed. */
39
+ relativePath: string;
40
+ /** The filename, no directories. */
41
+ name: string;
42
+ /** Lowercased extension INCLUDING the dot, or `""` for a file that has none. */
43
+ ext: string;
44
+ size: number;
45
+ mtimeMs: number;
46
+ /** Every other FILE name in the same directory — see {@link ScanOptions.captureSiblings}. */
47
+ siblings: string[];
48
+ }
49
+ export type DropOutcome =
50
+ /** Filed successfully. The original is deleted. */
51
+ {
52
+ kind: "stored";
53
+ detail?: string;
54
+ }
55
+ /** Already present at the destination. The original is deleted — that is the point. */
56
+ | {
57
+ kind: "duplicate";
58
+ detail?: string;
59
+ }
60
+ /** Not content: junk, an unusable sidecar, a format this app does not take. DELETED. */
61
+ | {
62
+ kind: "discard";
63
+ detail?: string;
64
+ }
65
+ /** Deliberately parked (a duplicate awaiting a decision). LEFT ALONE, not retried loudly. */
66
+ | {
67
+ kind: "held";
68
+ detail?: string;
69
+ }
70
+ /** Could not be filed. LEFT IN PLACE and reported. */
71
+ | {
72
+ kind: "failed";
73
+ detail: string;
74
+ };
75
+ export interface SweepReport {
76
+ scanned: number;
77
+ stored: number;
78
+ duplicate: number;
79
+ discarded: number;
80
+ held: number;
81
+ failed: {
82
+ relativePath: string;
83
+ error: string;
84
+ }[];
85
+ /** Files still being copied in — they are picked up by the next sweep, not an error. */
86
+ unstable: string[];
87
+ bytesRemoved: number;
88
+ emptyDirsRemoved: number;
89
+ /** Anything left in the root when the sweep finished. Empty is the goal. */
90
+ remaining: number;
91
+ }
92
+ export interface ScanOptions {
93
+ /** Two size samples this far apart prove a copy finished. */
94
+ stabilityMs?: number;
95
+ /** Names that are never content, on top of the built-in OS list. */
96
+ reservedNames?: string[];
97
+ /**
98
+ * Capture each directory's file listing on the ONE walk.
99
+ *
100
+ * 🔴 Load-bearing for sidecar art, and it cannot be done later. A folder's `cover.jpg` is
101
+ * only reachable while the directory still exists, and filing the audio out of it removes
102
+ * the neighbours — by the time anything else could look, they are gone. Capturing on the
103
+ * walk also costs one `readdir` per directory instead of one per file, which on a 6,000
104
+ * track library is the difference between seconds and minutes.
105
+ */
106
+ captureSiblings?: boolean;
107
+ /** Directory names to skip entirely at any depth (another app's queue, our own state). */
108
+ skipDirs?: string[];
109
+ /** Stop after this many files. Absent means everything. */
110
+ limit?: number;
111
+ }
112
+ /** A name that is OS noise, editor scratch, or a copy still in flight. */
113
+ export declare function isJunkName(name: string): boolean;
114
+ /**
115
+ * Every candidate file under `root`, deepest-last, with each one's sibling listing.
116
+ *
117
+ * Junk is NOT filtered out here — it is returned like anything else, because a sweep whose job
118
+ * includes *"if it's a wrong type of file delete it"* has to see it in order to delete it.
119
+ * {@link isJunkName} is exported so a handler can classify without re-deriving the list.
120
+ */
121
+ export declare function scanDropzone(root: string, opts?: ScanOptions): Promise<DroppedFile[]>;
122
+ /**
123
+ * How old an EMPTY file has to be before it is believed. See {@link isStable}.
124
+ *
125
+ * A file that has just been created and has no bytes yet is almost certainly mid-copy; the
126
+ * same file a minute later really is empty.
127
+ */
128
+ export declare const EMPTY_FILE_GRACE_MS = 60000;
129
+ /**
130
+ * Has this file finished being copied in? See rule 2 in the header.
131
+ *
132
+ * 🔴 A zero-byte file needs its own answer, and getting it wrong strands the folder forever.
133
+ * The obvious test — "same size twice AND non-empty" — treats a genuinely empty file as
134
+ * permanently mid-copy: it is never handled, never deleted, and so the dropzone never empties,
135
+ * which is the one outcome the owner's brief measures success by (*"in the end there should be
136
+ * nothing left"*). Measured on a first run: three real files, all 0 bytes, reported as "still
137
+ * copying" on every sweep for as long as they sat there.
138
+ *
139
+ * Emptiness is only evidence of an unfinished copy while the file is NEW, so age decides. A
140
+ * file created a minute ago with no bytes in it is empty, not arriving.
141
+ */
142
+ export declare function isStable(file: DroppedFile, stabilityMs?: number): Promise<boolean>;
143
+ /**
144
+ * Remove directories left empty by a sweep, deepest first — and never the root.
145
+ *
146
+ * 🔴 `rmdir` refuses a non-empty directory, and that refusal IS the safety property. A
147
+ * recursive delete here would take a file the sweep deliberately chose not to ingest — a held
148
+ * duplicate, a failure left in place for the owner to look at — which is precisely the set of
149
+ * files that must survive.
150
+ */
151
+ export declare function pruneEmptyDirs(root: string, dir?: string): Promise<number>;
152
+ /** What the app supplies: what to do with one file. */
153
+ export type DropHandler = (file: DroppedFile) => Promise<DropOutcome>;
154
+ /**
155
+ * Walk a dropzone, hand every file to `handle`, and leave nothing behind that was dealt with.
156
+ *
157
+ * 🔴 The DELETION is here, and it is deliberately not the handler's job. Every app would
158
+ * otherwise reimplement rule 1, and the one that got it wrong would lose the owner's only copy
159
+ * silently. A handler says what happened; this decides what that means for the file on disk.
160
+ */
161
+ export declare function sweepDropzone(root: string, handle: DropHandler, opts?: ScanOptions & {
162
+ dryRun?: boolean;
163
+ onProgress?: (done: number, total: number) => void;
164
+ }): Promise<SweepReport>;
165
+ /** A one-line summary — every consumer prints one, and they should read alike. */
166
+ export declare const describeSweep: (report: SweepReport) => string;