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,171 @@
1
+ // The binary-server transcode API as a plain job client — submit, poll, and the two
2
+ // derivative shapes the fleet actually asks for.
3
+ //
4
+ // It was already here, inlined inside `createBinaryServerProcessing`, but only reachable
5
+ // through that module's two seams (`VideoProcessor` / `ImageDerivatives`), which are shaped
6
+ // for the cursedbelt file CATALOGUE: they take a `MediaStore`, a `workDir`, an owner and a
7
+ // catalogue id. An app that stores binaries some other way — apps/notes keys attachments by
8
+ // sha256 through satellite-kit's binary-store and has no catalogue at all — could not reach
9
+ // the pipeline without either adopting the catalogue or writing a second HTTP client.
10
+ //
11
+ // So the transport lives here and the catalogue seams are built on top of it. There is ONE
12
+ // derivative pipeline in the fleet, and this is its entry point.
13
+ //
14
+ // ── Why binary-server rather than deriving locally ──────────────────────────────────────
15
+ // Derivatives are made where the SOURCE BYTES already are. binary-server is the fleet's
16
+ // binary store, it runs on a Mac with real CPU, disk, ffmpeg and (for HEIC) sips, and the
17
+ // derived bytes land back in the same tenant beside their source. Pulling a multi-GB video
18
+ // down to an app host to run ffmpeg there defeats the point, and the shared prod t3.micro
19
+ // has neither the disk nor the ffmpeg to do it at all.
20
+ //
21
+ // ── Who may call it ─────────────────────────────────────────────────────────────────────
22
+ // `/api/transcode` is server-to-server: it is gated on the shared internal secret AND, since
23
+ // the 2026-08-05 delete-surface hardening, 404s outright from binary-server's public host.
24
+ // A prod-hosted app therefore CANNOT drive it; the Mac side of an app must.
25
+
26
+ export type TranscodeJobStatus = 'pending' | 'processing' | 'ready' | 'failed';
27
+
28
+ export type TranscodeJobResult = {
29
+ durationSeconds?: number;
30
+ hasHls?: boolean;
31
+ hasPoster?: boolean;
32
+ hasSprite?: boolean;
33
+ hasThumbnail?: boolean;
34
+ hasDisplay?: boolean;
35
+ };
36
+
37
+ export type TranscodeJobStatusBody = {
38
+ status: TranscodeJobStatus;
39
+ progress?: number;
40
+ error?: string;
41
+ result?: TranscodeJobResult;
42
+ };
43
+
44
+ export type TranscodeJobClientConfig = {
45
+ /** e.g. 'https://binary-server.cursedalchemy.com' — same value as the byte store's baseUrl. */
46
+ baseUrl: string;
47
+ /** App namespace (path + token scope), e.g. 'notes'. Keys are sent app-scoped. */
48
+ appId: string;
49
+ /** Shared server-to-server secret (x-internal-secret). */
50
+ internalSecret: string;
51
+ /** Poll cadence while a job runs. */
52
+ pollMs?: number;
53
+ /** Give up after this long (the job keeps running server-side; the caller just stops waiting). */
54
+ timeoutMs?: number;
55
+ fetchImpl?: typeof fetch;
56
+ /** Injectable clock/sleep for tests. */
57
+ sleep?: (ms: number) => Promise<void>;
58
+ };
59
+
60
+ export type TranscodeJobClient = {
61
+ /** Prefix a bare key with this client's app namespace, as the API expects. */
62
+ appKey: (key: string) => string;
63
+ submit: (body: Record<string, unknown>) => Promise<string>;
64
+ poll: (jobId: string, onProgress?: (pct: number) => void) => Promise<TranscodeJobStatusBody>;
65
+ /**
66
+ * Still-image derivatives: a width-bounded JPEG thumbnail at `thumbKey`, plus — only for
67
+ * formats browsers cannot render (HEIC/HEIF) — a full-size display JPEG at `displayKey`.
68
+ * Keys are bare (this client scopes them).
69
+ */
70
+ imageThumbnail: (opts: {
71
+ sourceKey: string;
72
+ thumbKey: string;
73
+ displayKey?: string;
74
+ /** Job label in binary-server's queue; defaults to the source key. */
75
+ nodeId?: string;
76
+ }) => Promise<{ hasThumbnail: boolean; hasDisplay: boolean }>;
77
+ /**
78
+ * A video's poster frame ONLY — no ladder, no HLS, no sprite sheet. `posterOnly` collapses
79
+ * the library strategy to probe + poster, which is what a grid tile needs and is an order
80
+ * of magnitude cheaper than a transcode nobody asked for.
81
+ */
82
+ videoPoster: (opts: {
83
+ sourceKey: string;
84
+ posterKey: string;
85
+ nodeId?: string;
86
+ }) => Promise<{ hasPoster: boolean; durationSeconds: number }>;
87
+ };
88
+
89
+ const DEFAULT_POLL_MS = 1500;
90
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
91
+
92
+ export function createTranscodeJobClient(cfg: TranscodeJobClientConfig): TranscodeJobClient {
93
+ const fetchImpl = cfg.fetchImpl ?? fetch;
94
+ const sleep = cfg.sleep ?? ((ms: number) => new Promise((r) => setTimeout(r, ms)));
95
+ const pollMs = cfg.pollMs ?? DEFAULT_POLL_MS;
96
+ const timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS;
97
+ const appKey = (key: string) => `${cfg.appId}/${key}`;
98
+ const headers = {
99
+ 'content-type': 'application/json',
100
+ 'x-internal-secret': cfg.internalSecret,
101
+ };
102
+
103
+ const submit = async (body: Record<string, unknown>): Promise<string> => {
104
+ const res = await fetchImpl(`${cfg.baseUrl}/api/transcode`, {
105
+ method: 'POST',
106
+ headers,
107
+ body: JSON.stringify(body),
108
+ });
109
+ if (res.status !== 202) {
110
+ throw new Error(`binary-server transcode submit: ${res.status} ${await res.text()}`);
111
+ }
112
+ const { jobId } = (await res.json()) as { jobId: string };
113
+ return jobId;
114
+ };
115
+
116
+ const poll = async (
117
+ jobId: string,
118
+ onProgress?: (pct: number) => void,
119
+ ): Promise<TranscodeJobStatusBody> => {
120
+ const deadline = Date.now() + timeoutMs;
121
+ for (;;) {
122
+ const res = await fetchImpl(`${cfg.baseUrl}/api/transcode/${encodeURIComponent(jobId)}`, {
123
+ headers,
124
+ });
125
+ if (!res.ok) throw new Error(`binary-server transcode poll: ${res.status}`);
126
+ const job = (await res.json()) as TranscodeJobStatusBody;
127
+ if (job.status === 'ready' || job.status === 'failed') return job;
128
+ if (typeof job.progress === 'number') onProgress?.(job.progress);
129
+ if (Date.now() > deadline) throw new Error(`binary-server transcode ${jobId}: timed out`);
130
+ await sleep(pollMs);
131
+ }
132
+ };
133
+
134
+ const imageThumbnail: TranscodeJobClient['imageThumbnail'] = async (opts) => {
135
+ const jobId = await submit({
136
+ strategy: 'image',
137
+ nodeId: opts.nodeId ?? opts.sourceKey,
138
+ sourceKey: appKey(opts.sourceKey),
139
+ // The image strategy reads `binaryKey` as the source it fetches; the two are the same
140
+ // object here because nothing is being replaced, only derived from.
141
+ binaryKey: appKey(opts.sourceKey),
142
+ posterKey: appKey(opts.thumbKey),
143
+ ...(opts.displayKey ? { displayKey: appKey(opts.displayKey) } : {}),
144
+ });
145
+ const job = await poll(jobId);
146
+ if (job.status !== 'ready') return { hasThumbnail: false, hasDisplay: false };
147
+ return {
148
+ hasThumbnail: job.result?.hasThumbnail ?? false,
149
+ hasDisplay: job.result?.hasDisplay ?? false,
150
+ };
151
+ };
152
+
153
+ const videoPoster: TranscodeJobClient['videoPoster'] = async (opts) => {
154
+ const jobId = await submit({
155
+ strategy: 'library',
156
+ nodeId: opts.nodeId ?? opts.sourceKey,
157
+ sourceKey: appKey(opts.sourceKey),
158
+ binaryKey: appKey(opts.sourceKey),
159
+ posterKey: appKey(opts.posterKey),
160
+ posterOnly: true,
161
+ });
162
+ const job = await poll(jobId);
163
+ if (job.status !== 'ready') return { hasPoster: false, durationSeconds: 0 };
164
+ return {
165
+ hasPoster: job.result?.hasPoster ?? false,
166
+ durationSeconds: job.result?.durationSeconds ?? 0,
167
+ };
168
+ };
169
+
170
+ return { appKey, submit, poll, imageThumbnail, videoPoster };
171
+ }
@@ -0,0 +1,230 @@
1
+ // cursedbelt-server/storage — the byte-storage layer cursedbelt's media pipeline was
2
+ // missing. `server/media` (media-bun) owns the codec/pixel work; this owns *where the bytes
3
+ // live and how they're served*: a backend-agnostic MediaStore (disk / binary-server / S3), a
4
+ // Range→Response helper for seekable <video>, path-embedded stream tokens (so a media element
5
+ // with no Bearer can still be authorized), a source registry, and the ingest→derivatives
6
+ // pipeline glue. Ported/generalised from ca's proven media stack; the app maps its own records
7
+ // to a MediaSource. Pairs with the `PlayableVideo` contract in `cursedbelt-core/media`.
8
+ //
9
+ // 🔴 THIS FILE IS A PUBLISHED LEAF — `cursedbelt-server/storage/types` — AND IT MUST STAY
10
+ // IMPORT-FREE AT RUNTIME. `src/leafSubpathsImportNothing.spec.ts` imports it in a fixture with
11
+ // no packages resolvable at all and fails if it throws, so an `import` here that survives
12
+ // transpilation (anything that is not `import type`) turns that spec red rather than turning
13
+ // up in a consumer's install. The subpath exists because `./server/storage`'s own index is a
14
+ // barrel: it reaches `streamToken` → `auth/jwt` → jose, the whole SQLite file catalogue under
15
+ // `files/`, and a lazily-imported `sharp` — while an app that wants only the SHAPE of a file
16
+ // token (apps/collections wanted exactly `FileTokenClaims`, a type TypeScript erases) was made
17
+ // to install a one-time-password library to get it. Add types here freely; add a value import
18
+ // here never.
19
+
20
+ import type { ProcessingStatus } from 'cursedbelt-core/media';
21
+
22
+ /** Identity/tenant context threaded from an app's auth into every store/source call, so keys
23
+ * namespace per-owner and one tenant can never read another's media. */
24
+ export type MediaCtx = {
25
+ userId: string;
26
+ tenantId: string;
27
+ };
28
+
29
+ /**
30
+ * The byte-storage adapter. The caller owns the key namespace (see keyspace helpers). One
31
+ * interface, many backends. Web-standard streams throughout (Bun/Hono) — the stream hands
32
+ * straight to a `Response` body with no node-stream conversion.
33
+ */
34
+ export interface MediaStore {
35
+ /** Store bytes under an opaque key. */
36
+ put(key: string, bytes: Uint8Array, mime: string): Promise<void>;
37
+ /** Stream a body to storage without buffering it — for multi-GB writes (a transcode source,
38
+ * HLS segments) where a Uint8Array would be a full heap copy. */
39
+ putStream(
40
+ key: string,
41
+ body: ReadableStream<Uint8Array>,
42
+ mime: string,
43
+ opts?: { contentLength?: number },
44
+ ): Promise<void>;
45
+ /** Read bytes back, or null if the key is gone. */
46
+ get(key: string): Promise<Uint8Array | null>;
47
+ /** Best-effort delete; must not throw if the key is already absent. */
48
+ remove(key: string): Promise<void>;
49
+ /** Recursively delete everything under a key prefix (an HLS tree, a user's media). */
50
+ removePrefix(prefix: string): Promise<void>;
51
+ /** Total byte size of a stored object, or null if gone. Answers Range requests without
52
+ * reading the whole object first. */
53
+ stat(key: string): Promise<{ size: number } | null>;
54
+ /** A web stream for the inclusive byte range [start, end], or the whole object when `range`
55
+ * is omitted; null if gone. Serves 206 Partial Content for seeking. */
56
+ getRange(
57
+ key: string,
58
+ range?: { start: number; end: number },
59
+ ): Promise<{ stream: ReadableStream<Uint8Array> } | null>;
60
+ /** Absolute on-disk path for a key if this backend is local — lets ffmpeg read the source
61
+ * directly. null/absent for remote backends (the pipeline downloads the source first). */
62
+ localPath?(key: string): Promise<string | null>;
63
+ /** For a user-facing download, some backends (binary-server) prefer redirecting the browser
64
+ * to a signed edge URL instead of proxying bytes. Returns the URL, or null to fall back. */
65
+ redirectUrl?(key: string, opts: { mime: string; filename?: string }): Promise<string | null>;
66
+ }
67
+
68
+ // ── binary-server file tokens ─────────────────────────────────────────────────────
69
+ // Declared HERE rather than beside the adapter that uses them (`binaryServerStore.ts`, which
70
+ // re-exports them for compatibility) because these three are the shapes a consumer needs
71
+ // WITHOUT the adapter: apps/collections' `binaryStore.ts` mints its own tokens and imports
72
+ // `FileTokenClaims` alone.
73
+
74
+ /**
75
+ * Claims binary-server verifies (see binary-server/src/tokens.ts). This is the ONE
76
+ * canonical shape for a binary-server file token across the fleet: cursedbelt's
77
+ * injected-signer `createBinaryServerStore` and `@satellites/satellite-kit`'s
78
+ * self-signing `createBinaryStore` both mint tokens against the SAME binary-server, so
79
+ * the claim set must never drift — satellite-kit imports this exact type rather than
80
+ * redeclaring it, so a token one side signs is always shaped for the other to read. The
81
+ * app's signer turns these into an RS256 JWT.
82
+ */
83
+ export type FileTokenClaims = {
84
+ /** Exact authorized path, app-prefixed: `<app>/<key>`. */
85
+ k?: string;
86
+ /** Authorized path prefix, app-prefixed (container trees, e.g. an HLS tree). */
87
+ p?: string;
88
+ /** Acting user/app id — audit trail on binary-server's events, never authz. */
89
+ uid?: string;
90
+ /** Chunk-session id (must match across every chunk of one upload). */
91
+ sid?: string;
92
+ /** Chunk index (0-based). */
93
+ ci?: number;
94
+ /** Total chunk count. */
95
+ tc?: number;
96
+ /** Download filename → Content-Disposition. */
97
+ fn?: string;
98
+ dl?: 'inline' | 'attachment';
99
+ /** Per-op scope. A DELETE against binary-server requires a token carrying `op: 'delete'`
100
+ * (in addition to the internal secret) — a plain read/download token can never be replayed
101
+ * as a delete, and the token's issuer + `k`/`p` tenant-scope the delete. Owner ruling #10. */
102
+ op?: 'delete';
103
+ };
104
+
105
+ export type FileTokenSigner = (claims: FileTokenClaims) => Promise<string>;
106
+
107
+ export type BinaryServerConfig = {
108
+ /** e.g. 'https://binary-server.cursedalchemy.com'. */
109
+ baseUrl: string;
110
+ /** App namespace (path + token scope), e.g. 'cursedalchemy'. */
111
+ appId: string;
112
+ /** App-provided RS256 signer (app holds the private key). */
113
+ signFileToken: FileTokenSigner;
114
+ /** Shared secret for internal ops (delete). Sent as x-internal-secret. */
115
+ internalSecret?: string;
116
+ };
117
+
118
+ /** A source of videos for a gallery/catalogue. Apps implement this differently (a disk folder,
119
+ * a binary-server app, ca's Files tree + private set). Consumers are source-agnostic. */
120
+ export interface MediaSource {
121
+ id: string;
122
+ title: string;
123
+ list(
124
+ ctx: MediaCtx,
125
+ opts: {
126
+ cursor?: string;
127
+ tag?: string;
128
+ query?: string;
129
+ limit?: number;
130
+ /** Listing order (backend maps to its own sorts; default newest-first). `random` is a
131
+ * seeded shuffle (stable across its own pagination); `lastViewed` orders by watch-open
132
+ * recency. A cursor is only valid under the sort that minted it. */
133
+ sort?: 'recent' | 'oldest' | 'name' | 'size' | 'random' | 'lastViewed';
134
+ },
135
+ ): Promise<{ items: import('cursedbelt-core/media').PlayableVideo[]; nextCursor?: string }>;
136
+ resolve(ctx: MediaCtx, videoId: string): Promise<import('cursedbelt-core/media').PlayableVideo | null>;
137
+ tags?(ctx: MediaCtx): Promise<string[]>;
138
+ ingest?(
139
+ ctx: MediaCtx,
140
+ upload: { name: string; mime: string; body: ReadableStream<Uint8Array>; size?: number },
141
+ ): Promise<import('cursedbelt-core/media').PlayableVideo>;
142
+ remove?(ctx: MediaCtx, videoId: string): Promise<void>;
143
+ /** Record a watch-open of a video — the signal the `lastViewed` sort orders by. Optional: a
144
+ * read-only source (a disk folder) has nothing to record. */
145
+ recordView?(ctx: MediaCtx, videoId: string): Promise<void>;
146
+ }
147
+
148
+ /** A source of photos for an image gallery — the image sibling of {@link MediaSource}. Same
149
+ * shape (cursor listing, tag facets, ingest, view ledger), photo-typed items. */
150
+ export interface PhotoSource {
151
+ id: string;
152
+ title: string;
153
+ list(
154
+ ctx: MediaCtx,
155
+ opts: {
156
+ cursor?: string;
157
+ tag?: string;
158
+ query?: string;
159
+ limit?: number;
160
+ sort?: 'recent' | 'oldest' | 'name' | 'size' | 'random' | 'lastViewed';
161
+ },
162
+ ): Promise<{ items: import('cursedbelt-core/media').GalleryPhoto[]; nextCursor?: string }>;
163
+ resolve(ctx: MediaCtx, photoId: string): Promise<import('cursedbelt-core/media').GalleryPhoto | null>;
164
+ tags?(ctx: MediaCtx): Promise<string[]>;
165
+ ingest?(
166
+ ctx: MediaCtx,
167
+ upload: {
168
+ name: string;
169
+ mime: string;
170
+ body: ReadableStream<Uint8Array>;
171
+ size?: number;
172
+ tags?: string[];
173
+ },
174
+ ): Promise<import('cursedbelt-core/media').GalleryPhoto>;
175
+ remove?(ctx: MediaCtx, photoId: string): Promise<void>;
176
+ recordView?(ctx: MediaCtx, photoId: string): Promise<void>;
177
+ }
178
+
179
+ /** A source of MIXED media (photos + videos) for a combined gallery — the union of
180
+ * {@link MediaSource} and {@link PhotoSource} over ONE catalogue. `list`/`resolve`
181
+ * yield `GalleryMedia` (a `mediaKind`-tagged photo|video), and a `kind` filter narrows
182
+ * to just photos or just videos. Same cursor/tag/sort listing contract as its siblings. */
183
+ export interface MediaGallerySource {
184
+ id: string;
185
+ title: string;
186
+ list(
187
+ ctx: MediaCtx,
188
+ opts: {
189
+ /** Narrow the mixed feed — `all` (default) lists photos + videos. */
190
+ kind?: import('cursedbelt-core/media').MediaKindFilter;
191
+ cursor?: string;
192
+ tag?: string;
193
+ query?: string;
194
+ limit?: number;
195
+ sort?: 'recent' | 'oldest' | 'name' | 'size' | 'random' | 'lastViewed';
196
+ },
197
+ ): Promise<{ items: import('cursedbelt-core/media').GalleryMedia[]; nextCursor?: string }>;
198
+ resolve(ctx: MediaCtx, itemId: string): Promise<import('cursedbelt-core/media').GalleryMedia | null>;
199
+ tags?(ctx: MediaCtx): Promise<string[]>;
200
+ remove?(ctx: MediaCtx, itemId: string): Promise<void>;
201
+ recordView?(ctx: MediaCtx, itemId: string): Promise<void>;
202
+ }
203
+
204
+ export type { ProcessingStatus };
205
+
206
+ // ── Keyspace helpers ──────────────────────────────────────────────────────────────
207
+ // One source of truth for every storage key. Sources + serve routes + the pipeline all
208
+ // address bytes through these, so a key layout is never hand-spelled twice.
209
+ export const fsSourceKey = (owner: string, id: string) => `source/${owner}/${id}`;
210
+ export const fsMp4Key = (owner: string, id: string) => `fs/${owner}/${id}.mp4`;
211
+ export const fsHlsPrefix = (owner: string, id: string) => `hls/${owner}/${id}`;
212
+ export const fsHlsMasterKey = (owner: string, id: string) => `hls/${owner}/${id}/master.m3u8`;
213
+ export const fsHlsFileKey = (owner: string, id: string, rel: string) => `hls/${owner}/${id}/${rel}`;
214
+ export const fsPosterKey = (owner: string, id: string) => `fs/${owner}/${id}-poster.jpg`;
215
+ /** Full-size browser-safe JPEG derivative for images the browser can't render natively (HEIC/HEIF).
216
+ * The lightbox's full-view URL points here instead of the raw original. */
217
+ export const fsDisplayKey = (owner: string, id: string) => `fs/${owner}/${id}-display.jpg`;
218
+ export const fsSpriteKey = (owner: string, id: string) => `fs/${owner}/${id}-sprites.jpg`;
219
+ export const fsVttKey = (owner: string, id: string) => `fs/${owner}/${id}-sprites.vtt`;
220
+ /** Private (encrypted) namespace — the private-media layer stores ciphertext here. The HLS
221
+ * segments under it are AES-128-encrypted at rest (the player fetches the content key via the
222
+ * gated key route and decrypts); a distinct keyspace from public `hls/` so the two never collide. */
223
+ export const privateFsKey = (owner: string, id: string) => `pfs/${owner}/${id}`;
224
+ export const privateHlsPrefix = (owner: string, id: string) => `pfs/${owner}/${id}/hls`;
225
+ export const privateHlsMasterKey = (owner: string, id: string) =>
226
+ `pfs/${owner}/${id}/hls/master.m3u8`;
227
+ export const privateHlsFileKey = (owner: string, id: string, rel: string) =>
228
+ `pfs/${owner}/${id}/hls/${rel}`;
229
+ export const privatePosterKey = (owner: string, id: string) => `pfs/${owner}/${id}/poster.enc`;
230
+ export const privateSourceKey = (owner: string, id: string) => `pfs/${owner}/${id}/source.enc`;
@@ -0,0 +1,112 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import {
3
+ DERIVATIVE_SUFFIXES,
4
+ hlsMasterKeyFor,
5
+ hlsPrefixFor,
6
+ isDerivativeKey,
7
+ posterKeyFor,
8
+ spriteKeyFor,
9
+ spriteVttKeyFor,
10
+ VIDEO_KEY_GENERATION,
11
+ videoGenerationTag,
12
+ webPlayableKeyFor,
13
+ } from './videoDerivativeKeys';
14
+
15
+ /**
16
+ * The derivative NAME contract, pinned because three repos depend on it agreeing:
17
+ * binary-server writes these names, satellite-kit re-exports them, and this package mints the
18
+ * tokens that authorize them. A rename that looks harmless here is a silent no-preview
19
+ * everywhere else — the absence of an object is the "not ready yet" state, so a consumer
20
+ * asking for the wrong name cannot tell the difference between "renamed" and "still building".
21
+ */
22
+ describe('video derivative keys', () => {
23
+ const key = 'real/media/9295c424-8e55-445b-b4e4-1e7587394dfc';
24
+
25
+ it('names each derivative after its source', () => {
26
+ expect(webPlayableKeyFor(key)).toBe(`${key}.web.mp4`);
27
+ expect(spriteKeyFor(key)).toBe(`${key}.sprite.jpg`);
28
+ expect(spriteVttKeyFor(key)).toBe(`${key}.sprite.vtt`);
29
+ expect(posterKeyFor(key)).toBe(`${key}.thumb.jpg`);
30
+ });
31
+
32
+ it('🔴 puts the whole HLS ladder in ONE container', () => {
33
+ // binary-server co-locates a container's subtree under one shard and removes it
34
+ // atomically. Segments under sibling keys would be neither, and half a ladder is worse
35
+ // than none: the master keeps advertising rungs whose segments 404, which a player
36
+ // retries rather than falls back from.
37
+ expect(hlsPrefixFor(key)).toBe(`${key}.hls/`);
38
+ expect(hlsMasterKeyFor(key)).toBe(`${key}.hls/master.m3u8`);
39
+ expect(hlsMasterKeyFor(key).startsWith(hlsPrefixFor(key))).toBe(true);
40
+ });
41
+
42
+ it('the prefix ends in a slash, so a token for it cannot match a sibling key', () => {
43
+ // `${key}.hls` without the slash is a prefix of `${key}.hlsomething` too. The trailing
44
+ // slash is what keeps a prefix-scoped token confined to the tree it was minted for.
45
+ expect(hlsPrefixFor(key).endsWith('/')).toBe(true);
46
+ });
47
+
48
+ describe('isDerivativeKey', () => {
49
+ it('recognises every suffix it publishes', () => {
50
+ for (const suffix of DERIVATIVE_SUFFIXES)
51
+ expect(isDerivativeKey(`${key}${suffix}`)).toBe(true);
52
+ });
53
+
54
+ it('🔴 recognises objects INSIDE an HLS tree, which carry no derivative suffix', () => {
55
+ // Miss these and the sweep treats a segment as a video needing a remux, then derives
56
+ // from that, forever. None of them ends in a name from DERIVATIVE_SUFFIXES.
57
+ expect(isDerivativeKey(`${key}.hls/master.m3u8`)).toBe(true);
58
+ expect(isDerivativeKey(`${key}.hls/720/index.m3u8`)).toBe(true);
59
+ expect(isDerivativeKey(`${key}.hls/720/000.ts`)).toBe(true);
60
+ });
61
+
62
+ it('leaves an uploaded original alone', () => {
63
+ expect(isDerivativeKey(key)).toBe(false);
64
+ expect(isDerivativeKey('file/holiday.mp4')).toBe(false);
65
+ // A name that merely mentions one of the words is not a derivative.
66
+ expect(isDerivativeKey('file/sprite-collection.mov')).toBe(false);
67
+ });
68
+ });
69
+ });
70
+
71
+ /**
72
+ * ── 🔴 THE READER SIDE OF THE ENCODER GENERATION ────────────────────────────────────────
73
+ *
74
+ * binary-server writes these names; this module computes them; `notes`, `collections` and
75
+ * `family` read them through `@satellites/satellite-kit/video-derivatives`. Since 2026-09-09
76
+ * a video derivative's name carries the encoder generation that built it, so that a re-encode
77
+ * lands at a NEW address and an `immutable` cache lifetime is safe — `immutable` cannot be
78
+ * revoked for a copy already in somebody's browser.
79
+ *
80
+ * These tests pin the two things that make the three-repo contract hold.
81
+ */
82
+ describe("the encoder generation in a video derivative's name", () => {
83
+ it("🔴 generation 1 is UNTAGGED — these are the names every stored object already has", () => {
84
+ // If this changes, every derivative binary-server holds becomes unreachable from every
85
+ // app at once, and the whole library re-encodes to produce byte-identical output.
86
+ expect(webPlayableKeyFor("v/clip.mov")).toBe("v/clip.mov.web.mp4");
87
+ expect(webPlayableKeyFor("v/clip.mov", 1)).toBe("v/clip.mov.web.mp4");
88
+ expect(hlsMasterKeyFor("v/clip.mov")).toBe("v/clip.mov.hls/master.m3u8");
89
+ expect(hlsPrefixFor("v/clip.mov")).toBe("v/clip.mov.hls/");
90
+ expect(videoGenerationTag(1)).toBe("");
91
+ });
92
+
93
+ it("🔴 matches binary-server's VIDEO_ENCODER_GENERATION, which is 1", () => {
94
+ // The two constants live in different repos and neither can import the other.
95
+ // binary-server's encoderGeneration.spec.ts goes red when its number changes and
96
+ // names THIS file; this is the other end of that wire.
97
+ expect(VIDEO_KEY_GENERATION).toBe(1);
98
+ });
99
+
100
+ it("a bump gives the new bytes a new address", () => {
101
+ expect(webPlayableKeyFor("v/clip.mov", 2)).toBe("v/clip.mov.g2.web.mp4");
102
+ expect(hlsMasterKeyFor("v/clip.mov", 2)).toBe("v/clip.mov.g2.hls/master.m3u8");
103
+ });
104
+
105
+ it("🔴 a tagged derivative is still recognised as a derivative", () => {
106
+ // Miss this and a sweep treats clip.g2.web.mp4 as a source and derives from it for ever.
107
+ expect(isDerivativeKey("v/clip.mov.g2.web.mp4")).toBe(true);
108
+ expect(isDerivativeKey("v/clip.mov.g2.hls/720/000.ts")).toBe(true);
109
+ expect(isDerivativeKey("v/clip.mov")).toBe(false);
110
+ expect(isDerivativeKey("v/holiday.g2.mov")).toBe(false);
111
+ });
112
+ });