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,285 @@
1
+ import type { PlayableVideo, ProcessingStatus } from 'cursedbelt-core/media';
2
+ import type { PrivateIndex } from '../../private-media/index-store';
3
+ import type { MediaCtx } from '../types';
4
+
5
+ // ── FileCatalogue: a tagged flat file system over the storage layer ──────────────
6
+ // A production-shaped, SQLite-backed catalogue of a user's binaries (images, video, audio,
7
+ // docs, code). Generalises the video-only gallery source into ONE source of truth: the gallery
8
+ // becomes a video-filtered view over it. Improves on ca's fs_nodes: FLAT (no folder tree), the
9
+ // smart-view `fileType` is STORED at write (not re-derived per request), tags are a JOIN table
10
+ // (indexed facets, not a JSON scan), listing is CURSOR-paginated, and processing state is typed
11
+ // columns — not an opaque JSON blob. Reuses MediaStore, the media pipeline, and private-media.
12
+
13
+ export type FileType = 'image' | 'video' | 'audio' | 'doc' | 'code' | 'other';
14
+
15
+ // ── Collections ───────────────────────────────────────────────────────────────────
16
+ /** User-defined groupings (Apple-Photos-style). `favorites` is a system singleton per owner;
17
+ * `custom` are free-form. */
18
+ export type CollectionKind = 'custom' | 'favorites';
19
+ /** `normal` — always visible; `unlock` — hidden unless caller passes `includeUnlockable: true`. */
20
+ export type CollectionVisibility = 'normal' | 'unlock';
21
+
22
+ export type FileCollection = {
23
+ id: string;
24
+ name: string;
25
+ kind: CollectionKind;
26
+ visibility: CollectionVisibility;
27
+ /** Live count of member files. */
28
+ fileCount: number;
29
+ createdAt: number;
30
+ updatedAt: number;
31
+ };
32
+
33
+ /** The clean, UI-facing record — explicit capability booleans, no storage keys leaked (ca's
34
+ * worst inconsistency was `thumbnailKey`-vs-`hasThumbnail` across two shapes). */
35
+ export type FileRecord = {
36
+ id: string;
37
+ name: string;
38
+ mime: string;
39
+ size: number;
40
+ fileType: FileType;
41
+ tags: string[];
42
+ isPrivate: boolean;
43
+ hasThumbnail: boolean;
44
+ hasHls: boolean;
45
+ hasSprites: boolean;
46
+ durationSeconds?: number;
47
+ processingStatus: ProcessingStatus;
48
+ processingProgress: number;
49
+ /** True when this file is a member of the caller's Favorites collection. */
50
+ favorite: boolean;
51
+ /** All collection ids this file belongs to (within the owner's scope). */
52
+ collectionIds: string[];
53
+ createdAt: number;
54
+ updatedAt: number;
55
+ /** Set once `trash()` soft-deletes this file (epoch ms) — cleared by `restore()`. Undefined for
56
+ * a live (non-trashed) file. */
57
+ trashedAt?: number;
58
+ };
59
+
60
+ /** A tag with how many files carry it — the facet the flat browser renders as filter chips. */
61
+ export type TagFacet = { tag: string; count: number };
62
+
63
+ /** A page of results with an opaque cursor for the next page (cursor-keyed, not OFFSET). */
64
+ export type FilePage = { items: FileRecord[]; nextCursor?: string };
65
+
66
+ /** Every listing order the catalogue understands — the single source of truth the route layer
67
+ * validates against and the `FileSort` union derives from. `recent` = newest first (default);
68
+ * `oldest` = oldest first; `name` = A→Z; `size` = largest first; `random` = a seeded shuffle
69
+ * (the seed is minted per session and folded into the cursor, so a visit reshuffles but its own
70
+ * pagination is stable); `lastViewed` = most-recently-opened first (never-viewed files last). */
71
+ export const FILE_SORTS = ['recent', 'oldest', 'name', 'size', 'random', 'lastViewed'] as const;
72
+
73
+ /** Listing order. Every sort is cursor-paginated (the cursor encodes the sort key + id tie-break,
74
+ * plus the shuffle seed for `random`). A cursor is only valid under the sort that minted it. */
75
+ export type FileSort = (typeof FILE_SORTS)[number];
76
+
77
+ export type ListFilesOptions = {
78
+ /** Smart-view filter (indexed). */
79
+ type?: FileType;
80
+ /** Single-tag filter (indexed via the join table). */
81
+ tag?: string;
82
+ /** Name substring (public files only — private names are sealed). */
83
+ query?: string;
84
+ /** Include the caller's private files (requires an unlocked set — the caller resolves which
85
+ * ones actually open). Omitted ⇒ public only, matching ca's default listing. */
86
+ includePrivate?: boolean;
87
+ /** Filter to members of a specific collection. An unlock-gated collection yields empty unless
88
+ * `includeUnlockable` is also true. */
89
+ collectionId?: string;
90
+ /** Filter to files that are favorited (i.e. members of the owner's Favorites collection). */
91
+ favorite?: boolean;
92
+ /** Expose unlock-gated collections/results. Default false — the route layer gates this. */
93
+ includeUnlockable?: boolean;
94
+ /** Listing order (default 'recent'). A cursor is only valid for the sort it was minted under. */
95
+ sort?: FileSort;
96
+ cursor?: string;
97
+ limit?: number;
98
+ /** Browse the trash bin instead of the live library: true ⇒ ONLY trashed rows, omitted/false ⇒
99
+ * ONLY live rows (the default — trashed files never appear in a normal listing). */
100
+ trashedOnly?: boolean;
101
+ };
102
+
103
+ const DOC_MIME = /pdf|word|excel|spreadsheet|presentation|csv|officedocument|rtf|epub|^text\//;
104
+
105
+ /** Server-authoritative smart-view classification, computed ONCE at write time and stored.
106
+ * `isText` (a non-binary inline file) ⇒ code; else by mime. */
107
+ export function classifyFileType(mime: string, isText = false): FileType {
108
+ if (mime.startsWith('image/')) return 'image';
109
+ if (mime.startsWith('video/')) return 'video';
110
+ if (mime.startsWith('audio/')) return 'audio';
111
+ if (isText) return 'code';
112
+ if (DOC_MIME.test(mime)) return 'doc';
113
+ return 'other';
114
+ }
115
+
116
+ /** The catalogue contract. A SQLite implementation is the production default; the interface lets
117
+ * an app swap it. All calls are owner-scoped via {@link MediaCtx} (one tenant never sees another). */
118
+ /** Cheap owner-scoped aggregates (COUNT/SUM over the files table) — the admin
119
+ * surface's "site stats" source. Private items report count + bytes only
120
+ * (names/kinds stay sealed). */
121
+ export type CatalogueStats = {
122
+ files: number;
123
+ bytes: number;
124
+ byType: Partial<Record<FileType, number>>;
125
+ privateItems: number;
126
+ privateBytes: number;
127
+ processing: number;
128
+ failed: number;
129
+ };
130
+
131
+ export interface FileCatalogue {
132
+ list(ctx: MediaCtx, opts?: ListFilesOptions): Promise<FilePage>;
133
+ get(ctx: MediaCtx, id: string): Promise<FileRecord | null>;
134
+ /** Owner-scoped aggregates for admin/stats surfaces. */
135
+ stats(ctx: MediaCtx): Promise<CatalogueStats>;
136
+ tagFacets(ctx: MediaCtx, opts?: { includePrivate?: boolean }): Promise<TagFacet[]>;
137
+ /** Ingest a public upload: persist bytes, classify, kick off processing (thumbnail/HLS). */
138
+ ingest(
139
+ ctx: MediaCtx,
140
+ upload: {
141
+ name: string;
142
+ mime: string;
143
+ body: ReadableStream<Uint8Array>;
144
+ size?: number;
145
+ tags?: string[];
146
+ },
147
+ ): Promise<FileRecord>;
148
+ /** Register bytes uploaded OUT OF BAND (a chunked upload sent direct to the backend) — insert the
149
+ * row + kick processing WITHOUT re-storing bytes (they already sit at the catalogue's key for this
150
+ * id: fsSourceKey for video, fileBinaryKey otherwise). */
151
+ registerUploaded(
152
+ ctx: MediaCtx,
153
+ upload: { id: string; name: string; mime: string; size: number; tags?: string[] },
154
+ ): Promise<FileRecord>;
155
+ /** Rename / retag a file. */
156
+ update(
157
+ ctx: MediaCtx,
158
+ id: string,
159
+ patch: { name?: string; tags?: string[] },
160
+ ): Promise<FileRecord | null>;
161
+ /** Overwrite a file's BYTES in place — the identity (id, tags, collections, favorites, view
162
+ * history) survives while mime/size/fileType re-derive, stale derivatives are wiped, and
163
+ * processing re-kicks (video transcode / image thumbnail). Public files only — replacing an
164
+ * encrypted private item throws (delete + re-upload instead). Returns the refreshed record,
165
+ * or null if the id is unknown/other-owner. */
166
+ replaceContent(
167
+ ctx: MediaCtx,
168
+ id: string,
169
+ upload: { mime: string; body: ReadableStream<Uint8Array>; size?: number; name?: string },
170
+ ): Promise<FileRecord | null>;
171
+ /** PERMANENTLY delete a file + all its derivatives + tags + collection memberships. This is the
172
+ * hard-delete — a deliberate second action, typically only called on an already-trashed row
173
+ * (see {@link trash}). No-ops (never throws) for an unknown or other-owner id. */
174
+ remove(ctx: MediaCtx, id: string): Promise<void>;
175
+ /** Soft-delete: marks the file trashed (hidden from normal `list()`) without touching its bytes
176
+ * or metadata — recoverable via {@link restore}. The PRIMARY delete action a user takes.
177
+ * No-ops for an unknown/other-owner id or one already trashed. */
178
+ trash(ctx: MediaCtx, id: string): Promise<void>;
179
+ /** Undo `trash()`: clears trashed_at so the file reappears in normal listings. No-ops for an
180
+ * unknown/other-owner id. */
181
+ restore(ctx: MediaCtx, id: string): Promise<void>;
182
+ /** Ingest file(s) from a LOCAL path already on this host's disk (a directory imports every
183
+ * regular file inside it, recursively; a file imports just itself). Each imported file is
184
+ * tagged with its immediate containing folder name (the catalogue is flat — folder structure
185
+ * survives as a tag, not a tree) plus any `tags` passed. Unreadable/unsupported entries are
186
+ * skipped, not thrown — the return value reports both. */
187
+ importFromPath(
188
+ ctx: MediaCtx,
189
+ absPath: string,
190
+ opts?: { tags?: string[] },
191
+ ): Promise<{ imported: FileRecord[]; skipped: { path: string; reason: string }[] }>;
192
+ /** Recover from a restart that lost in-flight transcodes: re-kick public video rows still marked
193
+ * pending/processing (their source persists), and mark stuck private rows failed. Call once at
194
+ * app startup. Returns how many were re-queued vs. failed. */
195
+ recoverProcessing(): { requeued: number; failed: number };
196
+ /** Backfill grid thumbnails for finished public video rows that have no poster (landed while
197
+ * transcoding was off, or failed before the poster step). Re-kicks the poster pipeline for each.
198
+ * Returns how many were re-queued. */
199
+ backfillVideoPosters(): { requeued: number };
200
+ /** Backfill grid thumbnails for public image rows with no poster (e.g. HEIC photos whose sharp
201
+ * thumbnail failed before the ffmpeg fallback). Re-kicks the image pipeline. */
202
+ backfillImageThumbnails(): { requeued: number };
203
+
204
+ // ── Collections ────────────────────────────────────────────────────────────────
205
+ /** List the owner's collections. Unlock-gated ones are excluded unless `includeUnlockable`. */
206
+ listCollections(ctx: MediaCtx, opts?: { includeUnlockable?: boolean }): Promise<FileCollection[]>;
207
+ /** Create a custom collection. Name is trimmed; empty names throw. Duplicate names are allowed. */
208
+ createCollection(
209
+ ctx: MediaCtx,
210
+ input: { name: string; visibility?: CollectionVisibility },
211
+ ): Promise<FileCollection>;
212
+ /** Rename or change visibility of an owned collection. Returns null if not found. */
213
+ updateCollection(
214
+ ctx: MediaCtx,
215
+ id: string,
216
+ patch: { name?: string; visibility?: CollectionVisibility },
217
+ ): Promise<FileCollection | null>;
218
+ /** Delete a custom collection (and its memberships). Never deletes files. Favorites cannot be
219
+ * removed — throws an Error. */
220
+ removeCollection(ctx: MediaCtx, id: string): Promise<void>;
221
+ /** Add files to a collection. Idempotent (INSERT OR IGNORE). Unknown or other-owner file ids are
222
+ * silently skipped. */
223
+ addToCollection(ctx: MediaCtx, collectionId: string, fileIds: string[]): Promise<void>;
224
+ /** Remove files from a collection. Silently ignores ids not in the collection. */
225
+ removeFromCollection(ctx: MediaCtx, collectionId: string, fileIds: string[]): Promise<void>;
226
+ /** Toggle the favorite state of a file. Lazily creates the owner's Favorites collection on first
227
+ * use. */
228
+ setFavorite(ctx: MediaCtx, fileId: string, favorite: boolean): Promise<void>;
229
+
230
+ /** Record a view/watch-open of a file (upserts its last-viewed timestamp + bumps a count) — the
231
+ * signal the `lastViewed` sort orders by. No-ops for an unknown or other-owner file id. */
232
+ recordView(ctx: MediaCtx, fileId: string): Promise<void>;
233
+ }
234
+
235
+ /** Resolve a video FileRecord to a PlayableVideo (URLs baked server-side). Non-video ⇒ null. */
236
+ export type FileToPlayable = (ctx: MediaCtx, file: FileRecord) => Promise<PlayableVideo | null>;
237
+
238
+ // ── Private (encrypted) files ─────────────────────────────────────────────────────
239
+ // Private items live in the SAME catalogue (one source of truth), flagged is_private and holding
240
+ // a SEALED descriptor + WRAPPED content key instead of plaintext name/tags. The crypto plumbing
241
+ // (deriving the set key from a password/unlock token vs the server secret) stays at the route
242
+ // layer — the catalogue takes an already-derived `setKey: Buffer`. See private-media/crypto.
243
+
244
+ /** How a private item's content key is wrapped: `standard` (server-recoverable) vs `deniable`
245
+ * (password-derived; zero-knowledge). Mirrors private-media `KeyMode`. */
246
+ export type PrivateKeyMode = 'standard' | 'deniable';
247
+
248
+ /** The OPAQUE locked-index row — only non-sensitive metadata (so a user can prune media whose
249
+ * password is lost). NEVER name/tags/thumbnail: those are sealed. No set identifier is exposed. */
250
+ export type PrivateFileItem = {
251
+ id: string;
252
+ size: number;
253
+ createdAt: number;
254
+ processingStatus: ProcessingStatus;
255
+ processingProgress: number;
256
+ hasHls: boolean;
257
+ };
258
+
259
+ /** Private-media capability of a catalogue. `createSqliteFileCatalogue` implements this alongside
260
+ * {@link FileCatalogue}, storing private items as sealed rows in the SAME table (one source of
261
+ * truth). Membership is by decrypt-success — no stored password/set hash.
262
+ *
263
+ * Deliberately small: the catalogue owns only INGEST (the new private-upload path). Everything
264
+ * else — the locked index, unlock, try-a-password reveal, the gated content-key route, and the
265
+ * encrypted-HLS byte serving — is the existing `mountPrivateRoutes`, which operates through a
266
+ * {@link PrivateIndex}. `asPrivateIndex()` is the view that lets those routes read/write the
267
+ * catalogue's private rows, so there is exactly one store and one set of routes. */
268
+ export interface PrivateFileCatalogue {
269
+ /** Ingest an encrypted video: stage plaintext (never persisted), transcode to AES-128 HLS, seal
270
+ * the descriptor + wrap the content key under `setKey`, and insert a private row. */
271
+ ingestPrivate(
272
+ ctx: MediaCtx,
273
+ upload: {
274
+ name: string;
275
+ mime: string;
276
+ body: ReadableStream<Uint8Array>;
277
+ size?: number;
278
+ tags?: string[];
279
+ },
280
+ opts: { setKey: Buffer; keyMode: PrivateKeyMode },
281
+ ): Promise<PrivateFileItem>;
282
+ /** A {@link PrivateIndex} view over this catalogue's private rows — pass to `mountPrivateRoutes`
283
+ * so unlock/index/reveal/key/HLS all operate on the catalogue. `remove` also drops the bytes. */
284
+ asPrivateIndex(): PrivateIndex;
285
+ }
@@ -0,0 +1,81 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { mkdtemp, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { isFfmpegAvailable } from '../media-bun';
6
+ import { ffmpegImageThumbnail, isBrowserRenderableImage } from './imageThumbnail';
7
+
8
+ describe('isBrowserRenderableImage', () => {
9
+ test('HEIC/HEIF need a derivative; common web formats do not', () => {
10
+ for (const m of ['image/heic', 'image/heif', 'image/HEIC', 'image/heic-sequence'])
11
+ expect(isBrowserRenderableImage(m)).toBe(false);
12
+ for (const m of ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/avif'])
13
+ expect(isBrowserRenderableImage(m)).toBe(true);
14
+ });
15
+ });
16
+
17
+ const haveFfmpeg = await isFfmpegAvailable();
18
+
19
+ /** Synthesize a real HEIC via ffmpeg (libx265 in a HEIF container) — the format sharp's prebuilt
20
+ * binary refuses to decode, so it exercises the ffmpeg fallback path exactly. */
21
+ async function makeHeic(dest: string): Promise<boolean> {
22
+ const proc = Bun.spawn(
23
+ [
24
+ 'ffmpeg',
25
+ '-hide_banner',
26
+ '-y',
27
+ '-f',
28
+ 'lavfi',
29
+ '-i',
30
+ 'testsrc=duration=1:size=640x480:rate=1',
31
+ '-frames:v',
32
+ '1',
33
+ '-c:v',
34
+ 'libx265',
35
+ '-tag:v',
36
+ 'hvc1',
37
+ '-f',
38
+ 'hevc',
39
+ dest,
40
+ ],
41
+ { stdout: 'ignore', stderr: 'ignore' },
42
+ );
43
+ await proc.exited;
44
+ return proc.exitCode === 0;
45
+ }
46
+
47
+ // SKIPPED on a host without ffmpeg/ffprobe — it synthesizes and decodes real media. Runs by default wherever ffmpeg is installed.
48
+ describe.skipIf(!haveFfmpeg)('ffmpegImageThumbnail', () => {
49
+ test('decodes an HEVC/HEIC-style still into a width-capped JPEG', async () => {
50
+ const root = await mkdtemp(path.join(tmpdir(), 'cb-imgthumb-'));
51
+ try {
52
+ const src = path.join(root, 'in.heic');
53
+ if (!(await makeHeic(src))) return; // libx265 not built into this ffmpeg — skip
54
+ const bytes = new Uint8Array(await Bun.file(src).arrayBuffer());
55
+
56
+ const thumb = await ffmpegImageThumbnail(bytes, path.join(root, 'work'), 'img1', 512);
57
+
58
+ expect(thumb).not.toBeNull();
59
+ // JPEG SOI marker.
60
+ expect(thumb?.[0]).toBe(0xff);
61
+ expect(thumb?.[1]).toBe(0xd8);
62
+ expect((thumb as Uint8Array).byteLength).toBeGreaterThan(500);
63
+ } finally {
64
+ await rm(root, { recursive: true, force: true });
65
+ }
66
+ });
67
+
68
+ test('returns null (not throw) on undecodable bytes', async () => {
69
+ const root = await mkdtemp(path.join(tmpdir(), 'cb-imgthumb-bad-'));
70
+ try {
71
+ const thumb = await ffmpegImageThumbnail(
72
+ new Uint8Array([1, 2, 3, 4, 5]),
73
+ path.join(root, 'work'),
74
+ 'bad1',
75
+ );
76
+ expect(thumb).toBeNull();
77
+ } finally {
78
+ await rm(root, { recursive: true, force: true });
79
+ }
80
+ });
81
+ });
@@ -0,0 +1,60 @@
1
+ import { mkdir, rm } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { imageThumbnailArgs, runFfmpegProgress } from '../media-bun';
4
+
5
+ // Image mimes browsers CANNOT render inline (so the full-view <img> needs a JPEG derivative, not
6
+ // the original). Chrome/Firefox reject HEIC/HEIF; the rest (jpeg/png/webp/gif/avif/bmp/svg) render
7
+ // natively. Safari can show HEIC, but we normalize for everyone.
8
+ const BROWSER_INCOMPATIBLE_IMAGE_MIMES = new Set([
9
+ 'image/heic',
10
+ 'image/heif',
11
+ 'image/heic-sequence',
12
+ 'image/heif-sequence',
13
+ ]);
14
+
15
+ /** True when a browser can render this image mime inline (no JPEG derivative needed). */
16
+ export function isBrowserRenderableImage(mime: string): boolean {
17
+ return mime.startsWith('image/') && !BROWSER_INCOMPATIBLE_IMAGE_MIMES.has(mime.toLowerCase());
18
+ }
19
+
20
+ // Produce a width-capped JPEG thumbnail from arbitrary image bytes using ffmpeg — the fallback for
21
+ // formats sharp's prebuilt binary can't decode (notably HEIC/HEVC iPhone photos: the licensed-out
22
+ // libheif build only accepts AVIF). Best-effort: returns null on any failure. Uses a temp file
23
+ // because ffmpeg's HEIF demuxer needs seekable input (a pipe won't do).
24
+ export async function ffmpegImageThumbnail(
25
+ bytes: Uint8Array,
26
+ workDir: string,
27
+ id: string,
28
+ maxWidth = 512,
29
+ ): Promise<Uint8Array | null> {
30
+ const dir = path.join(workDir, '_imgthumb', id);
31
+ try {
32
+ await mkdir(dir, { recursive: true });
33
+ const inPath = path.join(dir, 'in.bin');
34
+ await Bun.write(inPath, bytes);
35
+ const outPath = path.join(dir, 'out.jpg');
36
+ let stderrTail = '';
37
+ const ok = await runFfmpegProgress(
38
+ imageThumbnailArgs(inPath, outPath, maxWidth),
39
+ 0,
40
+ undefined,
41
+ (tail) => {
42
+ stderrTail = tail;
43
+ },
44
+ );
45
+ if (!ok) {
46
+ // The "undecodable bytes" fixture test deliberately hits this path too — tagged
47
+ // `[expected-in-tests]` (grep-filterable) rather than silenced, so a real decode
48
+ // regression still surfaces.
49
+ console.warn(`[imageThumbnail][expected-in-tests] ffmpeg decode failed: ${stderrTail.slice(-400)}`);
50
+ return null;
51
+ }
52
+ const out = Bun.file(outPath);
53
+ if (!(await out.exists())) return null;
54
+ return new Uint8Array(await out.arrayBuffer());
55
+ } catch {
56
+ return null;
57
+ } finally {
58
+ await rm(dir, { recursive: true, force: true }).catch(() => {});
59
+ }
60
+ }
@@ -0,0 +1,122 @@
1
+ // cursedbelt-server/storage — byte-storage layer for the media pipeline: MediaStore adapters
2
+ // (disk / binary-server), a Range→Response helper, path-embedded stream tokens, a source
3
+ // registry, and the ingest→derivatives orchestrator. Builds on `server/media` (codec work) +
4
+ // `server/auth` (jose tokens). The `PlayableVideo` contract lives in `cursedbelt-core/media`.
5
+
6
+ // The derivative NAME contract — the seam between binary-server (which writes these names),
7
+ // satellite-kit (which re-exports them for the satellite apps) and this package's own token
8
+ // minting. Pure string transforms: nothing here can resolve a stored object.
9
+ export {
10
+ DERIVATIVE_SUFFIXES,
11
+ hlsMasterKeyFor,
12
+ hlsPrefixFor,
13
+ isDerivativeKey,
14
+ posterKeyFor,
15
+ spriteKeyFor,
16
+ spriteVttKeyFor,
17
+ webPlayableKeyFor,
18
+ } from './videoDerivativeKeys';
19
+ export type { BinaryServerConfig, FileTokenClaims, FileTokenSigner } from './binaryServerStore';
20
+ export { createBinaryServerStore } from './binaryServerStore';
21
+ export {
22
+ type BinaryServerProcessingConfig,
23
+ createBinaryServerProcessing,
24
+ } from './binaryServerProcessing';
25
+ // The transcode API as a plain job client, for apps whose binaries do NOT live in the file
26
+ // catalogue (apps/notes keys attachments by sha256). One derivative pipeline, two doors.
27
+ export {
28
+ createTranscodeJobClient,
29
+ type TranscodeJobClient,
30
+ type TranscodeJobClientConfig,
31
+ type TranscodeJobResult,
32
+ type TranscodeJobStatus,
33
+ type TranscodeJobStatusBody,
34
+ } from './transcodeJobs';
35
+ export { createDiskStore } from './diskStore';
36
+ // The FileCatalogue: a SQLite-backed tagged flat file system over this storage layer — one
37
+ // source of truth for a user's binaries (the video gallery is a video-filtered view over it).
38
+ export {
39
+ type CatalogueStats,
40
+ type ChunkedInit,
41
+ type ChunkedUploadService,
42
+ catalogueUploadKey,
43
+ classifyFileType,
44
+ createCatalogueMediaSource,
45
+ createCataloguePhotoSource,
46
+ createCatalogueVideoSource,
47
+ createChunkedUploadService,
48
+ createFilesStack,
49
+ createFileToPlayable,
50
+ createFileUrls,
51
+ createLedgeredDeletes,
52
+ createSqliteFileCatalogue,
53
+ createStorageOpsLedger,
54
+ createStorageReconciler,
55
+ FILE_CATALOGUE_MIGRATIONS,
56
+ type FileCatalogue,
57
+ type FilePage,
58
+ type FileRecord,
59
+ type FilesStack,
60
+ type FilesStackMountAuth,
61
+ type FilesStackOptions,
62
+ type FileToPlayable,
63
+ type FileType,
64
+ type FileUrls,
65
+ fileBinaryKey,
66
+ type ImageDerivatives,
67
+ type ImageDerivativesOptions,
68
+ type ListFilesOptions,
69
+ type MintChunkUpload,
70
+ type MountFileRoutesOptions,
71
+ mountFileRoutes,
72
+ type PrivateFileCatalogue,
73
+ type PrivateFileItem,
74
+ type PrivateKeyMode,
75
+ type SqliteFileCatalogueOptions,
76
+ type StorageAction,
77
+ type StorageOpRow,
78
+ type StorageOpsLedger,
79
+ type StorageReconciler,
80
+ type VideoProcessor,
81
+ } from './files';
82
+ export { type CorsResolver, type MountMediaOptions, mountMediaRoutes } from './mountMediaRoutes';
83
+ export {
84
+ createDiskSource,
85
+ createMediaUrls,
86
+ type DiskSource,
87
+ type MediaUrls,
88
+ SourceRegistry,
89
+ } from './sources';
90
+ // The saas profile's `ObjectStore` (08 §1b). Its local counterpart is
91
+ // `createFsObjectStore` in `cwip/storage` — same interface, different issuer.
92
+ export {
93
+ createS3ObjectStore,
94
+ type S3ClientLike,
95
+ type S3ObjectHandle,
96
+ type S3ObjectStoreOptions,
97
+ type S3ObjectWriter,
98
+ } from './s3ObjectStore';
99
+ export { decodeRange, type RangeDecision, type ServeRangeOpts, serveRange } from './streamRange';
100
+ export { createStreamTokenService, type StreamGrant, type StreamTokenService } from './streamToken';
101
+ export {
102
+ fsHlsFileKey,
103
+ fsHlsMasterKey,
104
+ fsHlsPrefix,
105
+ fsMp4Key,
106
+ fsPosterKey,
107
+ fsSourceKey,
108
+ fsSpriteKey,
109
+ fsVttKey,
110
+ type MediaCtx,
111
+ type MediaGallerySource,
112
+ type MediaSource,
113
+ type MediaStore,
114
+ type PhotoSource,
115
+ privateFsKey,
116
+ } from './types';
117
+ export {
118
+ type ProcessOptions,
119
+ type ProcessResult,
120
+ processVideo,
121
+ } from './videoProcessor';
122
+ export { objectStoreAsMediaStore } from './objectStoreMediaStore';