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,72 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { Hono } from 'hono';
3
+ import { ApiError, toErrorEnvelope } from '../errors';
4
+ import {
5
+ listSqliteTables,
6
+ NotReadOnlyError,
7
+ readSqliteTable,
8
+ runReadonlySqlite,
9
+ UnknownTableError,
10
+ } from './browse';
11
+
12
+ /**
13
+ * A read-only SQLite browser as a Hono sub-app — a thin HTTP wrapper over the
14
+ * Hono-free primitives in `browse.ts` (the same primitives a cursedshell plugin or
15
+ * a bespoke-transport app consumes directly). Mount it behind the owner-only guard:
16
+ *
17
+ * ```ts
18
+ * app.route('/api/v1/admin/sqlite', ownerOnly(createSqliteViewerMiddleware(db)));
19
+ * ```
20
+ *
21
+ * Three endpoints — list tables + counts, paginate one table, run an ad-hoc
22
+ * `SELECT` — letting the owner inspect the live DB without shell access. Every path
23
+ * is read-only (see `browse.ts` for the injection/read-only guards). Authn/authz is
24
+ * the mounting app's job (this sub-app trusts that it is already owner-gated).
25
+ */
26
+ export function createSqliteViewerMiddleware(db: Database): Hono {
27
+ const app = new Hono();
28
+
29
+ app.get('/tables', (c) => c.json({ items: listSqliteTables(db) }));
30
+
31
+ app.get('/tables/:table', (c) => {
32
+ try {
33
+ return c.json(
34
+ readSqliteTable(db, c.req.param('table'), {
35
+ limit: c.req.query('limit'),
36
+ offset: c.req.query('offset'),
37
+ }),
38
+ );
39
+ } catch (err) {
40
+ if (err instanceof UnknownTableError) {
41
+ return c.json(toErrorEnvelope(new ApiError(404, 'Unknown table', { code: 'unknown_table' })), 404);
42
+ }
43
+ throw err;
44
+ }
45
+ });
46
+
47
+ app.post('/query', async (c) => {
48
+ let payload: { sql?: unknown; params?: unknown };
49
+ try {
50
+ payload = await c.req.json();
51
+ } catch {
52
+ return c.json(toErrorEnvelope(new ApiError(400, 'Invalid JSON body', { code: 'bad_request' })), 400);
53
+ }
54
+ const params = Array.isArray(payload.params) ? (payload.params as unknown[]) : [];
55
+ try {
56
+ const items = runReadonlySqlite(db, payload.sql as string, params);
57
+ return c.json({ items });
58
+ } catch (err) {
59
+ if (err instanceof NotReadOnlyError) {
60
+ return c.json(toErrorEnvelope(new ApiError(400, err.message, { code: 'not_read_only' })), 400);
61
+ }
62
+ return c.json(
63
+ toErrorEnvelope(
64
+ new ApiError(400, err instanceof Error ? err.message : String(err), { code: 'query_failed' }),
65
+ ),
66
+ 400,
67
+ );
68
+ }
69
+ });
70
+
71
+ return app;
72
+ }
@@ -0,0 +1,222 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
2
+ import { existsSync, mkdirSync } from 'node:fs';
3
+ import { mkdtemp, rm } from 'node:fs/promises';
4
+ import { tmpdir } from 'node:os';
5
+ import path from 'node:path';
6
+ import { pickFreePort, startTestServer, type TestServer } from 'cwip/testing';
7
+ import { createBinaryServerProcessing } from './binaryServerProcessing';
8
+ import { fsHlsMasterKey, fsMp4Key, fsPosterKey } from './types';
9
+
10
+ // End-to-end proof of the REMOTE processing pipeline: the cb client submits
11
+ // library/image jobs to a REAL binary-server (booted from the local checkout,
12
+ // isolated storage), which runs real ffmpeg/sips and writes derivatives back
13
+ // into its own store. Skips cleanly when the checkout or ffmpeg is absent.
14
+ // Mirrors transcode.integration.spec.ts's harness.
15
+
16
+ const BS_DIR = process.env.BINARY_SERVER_DIR ?? '';
17
+ const BS_PRESENT = BS_DIR !== '' && existsSync(path.join(BS_DIR, 'src', 'index.ts'));
18
+ const FFMPEG_PRESENT = Bun.which('ffmpeg') !== null;
19
+
20
+ // SKIPPED until a real binary-server checkout is present: point BINARY_SERVER_DIR at one.
21
+ // There is deliberately no default — a published package must not go looking in one machine's
22
+ // directory layout — and `binary-server` has not been re-homed here, so this skips on every host today.
23
+ // ffmpeg must be on PATH too: the proof is that the REMOTE side runs real ffmpeg, not that the client posted a job.
24
+ describe.skipIf(!BS_PRESENT || !FFMPEG_PRESENT)(
25
+ 'binaryServerProcessing → real binary-server (integration)',
26
+ () => {
27
+ let port: number;
28
+ let storeDir: string;
29
+ let server: TestServer | null = null;
30
+ let tmpData: string;
31
+ const INTERNAL_SECRET = 'test-processing-secret';
32
+ const APP = 'ptest';
33
+
34
+ beforeAll(async () => {
35
+ tmpData = await mkdtemp(path.join(tmpdir(), 'bs-processing-test-'));
36
+ storeDir = path.join(tmpData, 'binaries');
37
+ mkdirSync(storeDir, { recursive: true });
38
+ port = await pickFreePort();
39
+ server = await startTestServer({
40
+ cwd: BS_DIR,
41
+ cmd: ['bun', path.join(BS_DIR, 'src', 'index.ts')],
42
+ port,
43
+ env: {
44
+ PORT: String(port),
45
+ HOST: '127.0.0.1',
46
+ STORAGE_ROOT: storeDir,
47
+ DB_PATH: path.join(tmpData, 'test.sqlite'),
48
+ INTERNAL_SECRET,
49
+ NODE_ENV: 'test',
50
+ },
51
+ healthPath: '/healthz',
52
+ timeoutMs: 90_000,
53
+ });
54
+
55
+ // Register the tenant, the way `register-app` does for a real one. This is
56
+ // not harness ceremony: since binary-server started INDEXING every transcode
57
+ // output (bs d7bf791), `file_index.app_id` has a FOREIGN KEY onto `apps`, so
58
+ // an unregistered tenant cannot have a derivative indexed at all. bs now
59
+ // refuses such a job up front rather than writing bytes it cannot index — and
60
+ // a harness that skips registration is testing a configuration no deployment
61
+ // has, which is exactly why this suite went red for a real product change.
62
+ const register = Bun.spawn(
63
+ [
64
+ 'bun',
65
+ '-e',
66
+ `const db = await import(${JSON.stringify(path.join(BS_DIR, 'src', 'db.ts'))});
67
+ db.initDb();
68
+ db.upsertApp({ appId: process.argv[1], issuer: process.argv[2] });`,
69
+ APP,
70
+ `https://${APP}.test`,
71
+ ],
72
+ {
73
+ cwd: BS_DIR,
74
+ env: { ...process.env, STORAGE_ROOT: storeDir, DB_PATH: path.join(tmpData, 'test.sqlite') },
75
+ stdout: 'inherit',
76
+ stderr: 'inherit',
77
+ },
78
+ );
79
+ expect(await register.exited).toBe(0);
80
+ }, 90_000);
81
+
82
+ afterAll(async () => {
83
+ await server?.stop();
84
+ await rm(tmpData, { recursive: true, force: true }).catch(() => void 0);
85
+ });
86
+
87
+ // Write bytes straight into the isolated store the way an upload would land
88
+ // them (the store shards by container hash — replicate via the server's own
89
+ // sharding by using the internal write path: PUT via upload requires tokens,
90
+ // so instead spawn a tiny helper inside BS_DIR that uses its storage module).
91
+ const putDirect = async (key: string, localFile: string) => {
92
+ // `bun -e` argv: [bunPath, ...args] — the passed args start at argv[1].
93
+ const helper = `
94
+ const { writeFile } = await import(${JSON.stringify(path.join(BS_DIR, 'src', 'storage.ts'))});
95
+ const [app, ...rest] = process.argv[1].split('/');
96
+ await writeFile(app, rest.join('/'), Bun.file(process.argv[2]));
97
+ `;
98
+ const proc = Bun.spawn(['bun', '-e', helper, `${APP}/${key}`, localFile], {
99
+ cwd: BS_DIR,
100
+ env: { ...process.env, STORAGE_ROOT: storeDir, DB_PATH: path.join(tmpData, 'test.sqlite') },
101
+ stdout: 'inherit',
102
+ stderr: 'inherit',
103
+ });
104
+ expect(await proc.exited).toBe(0);
105
+ };
106
+
107
+ const statKey = (key: string) =>
108
+ fetch(`http://localhost:${port}/internal/stat?key=${encodeURIComponent(`${APP}/${key}`)}`);
109
+
110
+ test(
111
+ 'library job: real video → faststart + HLS + poster on the bs store',
112
+ async () => {
113
+ // Synthesize a 2s test clip with ffmpeg (color bars + tone).
114
+ const src = path.join(tmpData, 'clip.mp4');
115
+ const gen = Bun.spawn(
116
+ [
117
+ 'ffmpeg',
118
+ '-y',
119
+ '-f',
120
+ 'lavfi',
121
+ '-i',
122
+ 'testsrc=duration=2:size=320x240:rate=24',
123
+ '-f',
124
+ 'lavfi',
125
+ '-i',
126
+ 'sine=frequency=440:duration=2',
127
+ '-c:v',
128
+ 'libx264',
129
+ '-pix_fmt',
130
+ 'yuv420p',
131
+ '-c:a',
132
+ 'aac',
133
+ src,
134
+ ],
135
+ { stdout: 'ignore', stderr: 'ignore' },
136
+ );
137
+ expect(await gen.exited).toBe(0);
138
+ await putDirect('source/u1/vid1', src);
139
+
140
+ const { videoProcessor } = createBinaryServerProcessing({
141
+ baseUrl: `http://localhost:${port}`,
142
+ appId: APP,
143
+ internalSecret: INTERNAL_SECRET,
144
+ ladderHeights: [240],
145
+ pollMs: 300,
146
+ });
147
+ const progress: number[] = [];
148
+ const result = await videoProcessor({
149
+ store: {} as never,
150
+ workDir: tmpData,
151
+ owner: 'u1',
152
+ id: 'vid1',
153
+ sourceKey: 'source/u1/vid1',
154
+ onProgress: (_s, p) => progress.push(p),
155
+ });
156
+ expect(result.status).toBe('ready');
157
+ expect(result.hasPoster).toBe(true);
158
+ expect(result.hasHls).toBe(true);
159
+ expect(result.durationSeconds).toBeGreaterThan(1);
160
+
161
+ // The derivatives exist in the bs store tree (sharded layout — assert via glob).
162
+ const files = await Array.fromAsync(
163
+ new Bun.Glob('**/*').scan({ cwd: path.join(storeDir, APP), onlyFiles: true }),
164
+ );
165
+ expect(files.some((f) => f.endsWith(fsMp4Key('u1', 'vid1').split('/').pop() as string))).toBe(true);
166
+ expect(files.some((f) => f.endsWith('master.m3u8'))).toBe(true);
167
+ expect(files.some((f) => f.endsWith('vid1-poster.jpg'))).toBe(true);
168
+ },
169
+ { timeout: 120_000 },
170
+ );
171
+
172
+ test(
173
+ 'image job: real JPEG → 512px thumbnail derivative',
174
+ async () => {
175
+ const srcImg = path.join(tmpData, 'photo.jpg');
176
+ const gen = Bun.spawn(
177
+ [
178
+ 'ffmpeg',
179
+ '-y',
180
+ '-f',
181
+ 'lavfi',
182
+ '-i',
183
+ 'testsrc=duration=1:size=1600x1200:rate=1',
184
+ '-frames:v',
185
+ '1',
186
+ srcImg,
187
+ ],
188
+ { stdout: 'ignore', stderr: 'ignore' },
189
+ );
190
+ expect(await gen.exited).toBe(0);
191
+ await putDirect('file/u1/img1', srcImg);
192
+
193
+ const { imageDerivatives } = createBinaryServerProcessing({
194
+ baseUrl: `http://localhost:${port}`,
195
+ appId: APP,
196
+ internalSecret: INTERNAL_SECRET,
197
+ pollMs: 300,
198
+ });
199
+ const result = await imageDerivatives({
200
+ store: {} as never,
201
+ workDir: tmpData,
202
+ owner: 'u1',
203
+ id: 'img1',
204
+ mime: 'image/jpeg',
205
+ binaryKey: 'file/u1/img1',
206
+ posterKey: fsPosterKey('u1', 'img1'),
207
+ displayKey: 'fs/u1/img1-display.jpg',
208
+ needsDisplay: false,
209
+ });
210
+ expect(result.hasThumbnail).toBe(true);
211
+ const files = await Array.fromAsync(
212
+ new Bun.Glob('**/*').scan({ cwd: path.join(storeDir, APP), onlyFiles: true }),
213
+ );
214
+ expect(files.some((f) => f.endsWith('img1-poster.jpg'))).toBe(true);
215
+ },
216
+ { timeout: 60_000 },
217
+ );
218
+
219
+ void statKey; // (reserved for a future /internal/stat probe)
220
+ void fsHlsMasterKey;
221
+ },
222
+ );
@@ -0,0 +1,150 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { createBinaryServerProcessing } from './binaryServerProcessing';
3
+ import type { MediaStore } from './types';
4
+
5
+ // Fake binary-server: records submissions, walks each job pending → processing → ready/failed.
6
+ function fakeBs(opts: {
7
+ result?: Record<string, unknown>;
8
+ fail?: boolean;
9
+ rejectSubmit?: boolean;
10
+ }) {
11
+ const submissions: Array<Record<string, unknown>> = [];
12
+ let polls = 0;
13
+ const fetchImpl = (async (input: RequestInfo | URL, init?: RequestInit) => {
14
+ const url = String(input);
15
+ if (url.endsWith('/api/transcode') && init?.method === 'POST') {
16
+ if (opts.rejectSubmit) return new Response('nope', { status: 401 });
17
+ submissions.push(JSON.parse(String(init.body)));
18
+ return Response.json({ jobId: 'job-1' }, { status: 202 });
19
+ }
20
+ polls++;
21
+ if (polls === 1) return Response.json({ status: 'processing', progress: 42 });
22
+ if (opts.fail) return Response.json({ status: 'failed', error: 'boom' });
23
+ return Response.json({ status: 'ready', result: opts.result ?? {} });
24
+ }) as typeof fetch;
25
+ return { submissions, fetchImpl, pollCount: () => polls };
26
+ }
27
+
28
+ const noopStore = {} as MediaStore;
29
+
30
+ const cfg = (fetchImpl: typeof fetch) => ({
31
+ baseUrl: 'https://bs.example.com',
32
+ appId: 'p',
33
+ internalSecret: 's3cret',
34
+ fetchImpl,
35
+ pollMs: 1,
36
+ sleep: async () => {},
37
+ });
38
+
39
+ describe('createBinaryServerProcessing — video', () => {
40
+ test('submits an app-scoped library job and maps the result flags', async () => {
41
+ const bs = fakeBs({
42
+ result: { durationSeconds: 12.5, hasHls: true, hasPoster: true, hasSprite: false },
43
+ });
44
+ const { videoProcessor } = createBinaryServerProcessing(cfg(bs.fetchImpl));
45
+ const progress: Array<[string, number]> = [];
46
+ const res = await videoProcessor({
47
+ store: noopStore,
48
+ workDir: '/tmp/unused',
49
+ owner: 'u1',
50
+ id: 'vid1',
51
+ sourceKey: 'source/u1/vid1',
52
+ onProgress: (s, p) => progress.push([s, p]),
53
+ });
54
+ expect(res).toEqual({
55
+ status: 'ready',
56
+ durationSeconds: 12.5,
57
+ hasHls: true,
58
+ hasSprite: false,
59
+ hasPoster: true,
60
+ });
61
+ const job = bs.submissions[0];
62
+ expect(job?.strategy).toBe('library');
63
+ expect(job?.sourceKey).toBe('p/source/u1/vid1');
64
+ expect(job?.sourceKeyOut).toBe('p/fs/u1/vid1.mp4');
65
+ expect(job?.hlsPrefix).toBe('p/hls/u1/vid1');
66
+ expect(job?.posterKey).toBe('p/fs/u1/vid1-poster.jpg');
67
+ expect(job?.posterOnly).toBe(false);
68
+ expect(progress.some(([s, p]) => s === 'processing' && p === 42)).toBe(true);
69
+ expect(progress.at(-1)).toEqual(['ready', 100]);
70
+ });
71
+
72
+ test('posterOnly rides through', async () => {
73
+ const bs = fakeBs({ result: { hasPoster: true, durationSeconds: 3 } });
74
+ const { videoProcessor } = createBinaryServerProcessing(cfg(bs.fetchImpl));
75
+ await videoProcessor({
76
+ store: noopStore,
77
+ workDir: '/tmp/unused',
78
+ owner: 'u1',
79
+ id: 'vid2',
80
+ sourceKey: 'source/u1/vid2',
81
+ posterOnly: true,
82
+ });
83
+ expect(bs.submissions[0]?.posterOnly).toBe(true);
84
+ });
85
+
86
+ test('a failed job maps to status failed (never throws into the catalogue)', async () => {
87
+ const bs = fakeBs({ fail: true });
88
+ const { videoProcessor } = createBinaryServerProcessing(cfg(bs.fetchImpl));
89
+ const res = await videoProcessor({
90
+ store: noopStore,
91
+ workDir: '/tmp/unused',
92
+ owner: 'u1',
93
+ id: 'vid3',
94
+ sourceKey: 'source/u1/vid3',
95
+ });
96
+ expect(res.status).toBe('failed');
97
+ });
98
+
99
+ test('a rejected submit maps to status failed', async () => {
100
+ const bs = fakeBs({ rejectSubmit: true });
101
+ const { videoProcessor } = createBinaryServerProcessing(cfg(bs.fetchImpl));
102
+ const res = await videoProcessor({
103
+ store: noopStore,
104
+ workDir: '/tmp/unused',
105
+ owner: 'u1',
106
+ id: 'vid4',
107
+ sourceKey: 'source/u1/vid4',
108
+ });
109
+ expect(res.status).toBe('failed');
110
+ });
111
+ });
112
+
113
+ describe('createBinaryServerProcessing — image', () => {
114
+ test('submits the image job with display key only when needed', async () => {
115
+ const bs = fakeBs({ result: { hasThumbnail: true } });
116
+ const { imageDerivatives } = createBinaryServerProcessing(cfg(bs.fetchImpl));
117
+ const res = await imageDerivatives({
118
+ store: noopStore,
119
+ workDir: '/tmp/unused',
120
+ owner: 'u1',
121
+ id: 'img1',
122
+ mime: 'image/heic',
123
+ binaryKey: 'file/u1/img1',
124
+ posterKey: 'fs/u1/img1-poster.jpg',
125
+ displayKey: 'fs/u1/img1-display.jpg',
126
+ needsDisplay: true,
127
+ });
128
+ expect(res).toEqual({ hasThumbnail: true });
129
+ expect(bs.submissions[0]?.strategy).toBe('image');
130
+ expect(bs.submissions[0]?.displayKey).toBe('p/fs/u1/img1-display.jpg');
131
+ });
132
+
133
+ test('browser-renderable image omits displayKey and failure maps to hasThumbnail false', async () => {
134
+ const bs = fakeBs({ fail: true });
135
+ const { imageDerivatives } = createBinaryServerProcessing(cfg(bs.fetchImpl));
136
+ const res = await imageDerivatives({
137
+ store: noopStore,
138
+ workDir: '/tmp/unused',
139
+ owner: 'u1',
140
+ id: 'img2',
141
+ mime: 'image/jpeg',
142
+ binaryKey: 'file/u1/img2',
143
+ posterKey: 'fs/u1/img2-poster.jpg',
144
+ displayKey: 'fs/u1/img2-display.jpg',
145
+ needsDisplay: false,
146
+ });
147
+ expect(res).toEqual({ hasThumbnail: false });
148
+ expect(bs.submissions[0]?.displayKey).toBeUndefined();
149
+ });
150
+ });
@@ -0,0 +1,127 @@
1
+ import type { ImageDerivatives, VideoProcessor } from './files/catalogue';
2
+ import { createTranscodeJobClient } from './transcodeJobs';
3
+ import { fsHlsPrefix, fsMp4Key, fsPosterKey, fsSpriteKey, fsVttKey } from './types';
4
+
5
+ // The cursedbelt file CATALOGUE's two derivative seams, bound to binary-server's transcode
6
+ // API. The transport itself — submit, poll, and the two derivative shapes — lives in
7
+ // `./transcodeJobs`, so an app that stores binaries WITHOUT a catalogue (apps/notes keys
8
+ // attachments by sha256 through satellite-kit's binary-store) can reach the same pipeline
9
+ // without adopting the catalogue or writing a second HTTP client. There is one derivative
10
+ // pipeline in the fleet; this file is the catalogue-shaped door onto it.
11
+ //
12
+ // Why remote at all: the bytes already live on the binary-server host, which has real CPU,
13
+ // disk and ffmpeg. Pulling a multi-GB source down to the app host defeats the point, and the
14
+ // shared prod t3.micro cannot do it at all.
15
+ //
16
+ // Strategies used here (binary-server side): `library` = faststart MP4 + HLS ladder + poster +
17
+ // sprite/VTT in one job (posterOnly collapses it to probe + poster); `image` = 512px grid
18
+ // thumbnail + optional full-size browser-safe display JPEG. The classic `faststart`/`hls`
19
+ // strategies predate these and stay untouched for existing consumers.
20
+
21
+ export type BinaryServerProcessingConfig = {
22
+ /** e.g. 'https://binary-server.cursedalchemy.com' — same value as the byte store's baseUrl. */
23
+ baseUrl: string;
24
+ /** App namespace (path + token scope), e.g. 'p'. Keys sent to the transcode API are
25
+ * app-scoped (`<appId>/<key>`), matching what the MediaStore adapter writes. */
26
+ appId: string;
27
+ /** Shared server-to-server secret (x-internal-secret). */
28
+ internalSecret: string;
29
+ /** HLS rung heights (passed through as the job's ladder). */
30
+ ladderHeights?: number[];
31
+ /** Poll cadence while a job runs. */
32
+ pollMs?: number;
33
+ /** Give up on a job after this long (it keeps running server-side; the row just fails). */
34
+ timeoutMs?: number;
35
+ fetchImpl?: typeof fetch;
36
+ /** Injectable clock/sleep for tests. */
37
+ sleep?: (ms: number) => Promise<void>;
38
+ };
39
+
40
+ export function createBinaryServerProcessing(cfg: BinaryServerProcessingConfig): {
41
+ videoProcessor: VideoProcessor;
42
+ imageDerivatives: ImageDerivatives;
43
+ } {
44
+ const jobs = createTranscodeJobClient(cfg);
45
+ const { appKey, submit, poll } = jobs;
46
+
47
+ const videoProcessor: VideoProcessor = async (opts) => {
48
+ const { owner, id, onProgress } = opts;
49
+ const failed = {
50
+ status: 'failed' as const,
51
+ durationSeconds: 0,
52
+ hasHls: false,
53
+ hasSprite: false,
54
+ hasPoster: false,
55
+ };
56
+ try {
57
+ onProgress?.('processing', 1);
58
+ const jobId = await submit({
59
+ strategy: 'library',
60
+ nodeId: id,
61
+ sourceKey: appKey(opts.sourceKey),
62
+ binaryKey: appKey(fsMp4Key(owner, id)),
63
+ sourceKeyOut: appKey(fsMp4Key(owner, id)),
64
+ hlsPrefix: appKey(fsHlsPrefix(owner, id)),
65
+ posterKey: appKey(fsPosterKey(owner, id)),
66
+ spriteKey: appKey(fsSpriteKey(owner, id)),
67
+ vttKey: appKey(fsVttKey(owner, id)),
68
+ posterOnly: opts.posterOnly === true,
69
+ settings: { ladder: opts.ladderHeights ?? [480, 720, 1080] },
70
+ });
71
+ const job = await poll(jobId, (pct) => onProgress?.('processing', Math.min(99, pct)));
72
+ if (job.status !== 'ready') {
73
+ // The "a failed job maps to status failed" fixture test deliberately drives this
74
+ // path — tagged `[expected-in-tests]` (grep-filterable) rather than silenced, so a
75
+ // real binary-server outage still surfaces.
76
+ console.warn(
77
+ `[binaryServerProcessing][expected-in-tests] video job failed for ${id}: ${job.error ?? '?'}`,
78
+ );
79
+ onProgress?.('failed', 0);
80
+ return failed;
81
+ }
82
+ onProgress?.('ready', 100);
83
+ return {
84
+ status: 'ready',
85
+ durationSeconds: job.result?.durationSeconds ?? 0,
86
+ hasHls: job.result?.hasHls ?? false,
87
+ hasSprite: job.result?.hasSprite ?? false,
88
+ hasPoster: job.result?.hasPoster ?? false,
89
+ };
90
+ } catch (err) {
91
+ // The "a rejected submit maps to status failed" fixture test deliberately drives this
92
+ // path — tagged `[expected-in-tests]` (grep-filterable) rather than silenced, so a
93
+ // real binary-server outage still surfaces.
94
+ console.warn(
95
+ `[binaryServerProcessing][expected-in-tests] video job errored for ${id}: ${err instanceof Error ? err.message : err}`,
96
+ );
97
+ onProgress?.('failed', 0);
98
+ return failed;
99
+ }
100
+ };
101
+
102
+ const imageDerivatives: ImageDerivatives = async (args) => {
103
+ try {
104
+ const jobId = await submit({
105
+ strategy: 'image',
106
+ nodeId: args.id,
107
+ sourceKey: appKey(args.binaryKey),
108
+ binaryKey: appKey(args.binaryKey),
109
+ posterKey: appKey(args.posterKey),
110
+ ...(args.needsDisplay ? { displayKey: appKey(args.displayKey) } : {}),
111
+ });
112
+ const job = await poll(jobId);
113
+ if (job.status !== 'ready' || !job.result?.hasThumbnail) return { hasThumbnail: false };
114
+ return { hasThumbnail: true };
115
+ } catch (err) {
116
+ // The "browser-renderable image ... failure maps to hasThumbnail false" fixture test
117
+ // deliberately drives this path — tagged `[expected-in-tests]` (grep-filterable)
118
+ // rather than silenced, so a real binary-server outage still surfaces.
119
+ console.warn(
120
+ `[binaryServerProcessing][expected-in-tests] image job errored for ${args.id}: ${err instanceof Error ? err.message : err}`,
121
+ );
122
+ return { hasThumbnail: false };
123
+ }
124
+ };
125
+
126
+ return { videoProcessor, imageDerivatives };
127
+ }
@@ -0,0 +1,102 @@
1
+ import type { BinaryServerConfig, MediaStore } from './types';
2
+
3
+ // The token shapes moved to `./types` (published as `cursedbelt-server/storage/types`) so an
4
+ // app can import the CLAIM SHAPE without importing this adapter's module graph. Re-exported
5
+ // here so every existing `from './binaryServerStore'` and the `./index` barrel keep resolving.
6
+ export type { BinaryServerConfig, FileTokenClaims, FileTokenSigner } from './types';
7
+
8
+ // binary-server MediaStore adapter. binary-server (Bun/SQLite, RS256 token-gated, Range-capable)
9
+ // is a production storage path: a home/edge file service the app signs short-lived tokens for.
10
+ // User-facing downloads REDIRECT (303) to the signed edge URL rather than proxying bytes back
11
+ // through the app; server-side reads (transcode source, checks) stream locally via getRange.
12
+ //
13
+ // The RS256 signer is INJECTED — the app holds the private key (binary-server holds only the
14
+ // public key). cursedbelt carries no key material and no asymmetric-crypto specifics; it just
15
+ // calls `signFileToken` with the claims binary-server expects.
16
+
17
+ export function createBinaryServerStore(cfg: BinaryServerConfig): MediaStore {
18
+ const appKey = (key: string) => `${cfg.appId}/${key}`;
19
+ const mediaUrl = async (key: string, extra?: { fn?: string; dl?: 'inline' | 'attachment' }) => {
20
+ const token = await cfg.signFileToken({ k: appKey(key), ...extra });
21
+ return `${cfg.baseUrl}/media/${appKey(key)}?token=${encodeURIComponent(token)}`;
22
+ };
23
+ const uploadUrl = async (key: string) => {
24
+ const token = await cfg.signFileToken({ k: appKey(key) });
25
+ return `${cfg.baseUrl}/upload/${appKey(key)}?token=${encodeURIComponent(token)}`;
26
+ };
27
+
28
+ return {
29
+ async put(key, bytes, mime) {
30
+ const res = await fetch(await uploadUrl(key), {
31
+ method: 'PUT',
32
+ body: new Blob([bytes as BlobPart], { type: mime }),
33
+ });
34
+ if (!res.ok) throw new Error(`binary-server put ${key}: ${res.status}`);
35
+ },
36
+ async putStream(key, body, mime, opts) {
37
+ const headers: Record<string, string> = { 'content-type': mime };
38
+ if (opts?.contentLength != null) headers['content-length'] = String(opts.contentLength);
39
+ const res = await fetch(await uploadUrl(key), {
40
+ method: 'PUT',
41
+ body,
42
+ headers,
43
+ // A web ReadableStream body requires duplex: 'half' per the fetch streaming spec.
44
+ duplex: 'half',
45
+ } as RequestInit);
46
+ if (!res.ok) throw new Error(`binary-server putStream ${key}: ${res.status}`);
47
+ },
48
+ async get(key) {
49
+ const res = await fetch(await mediaUrl(key));
50
+ if (res.status === 404) return null;
51
+ if (!res.ok) throw new Error(`binary-server get ${key}: ${res.status}`);
52
+ return new Uint8Array(await res.arrayBuffer());
53
+ },
54
+ async remove(key) {
55
+ // binary-server gates DELETE on BOTH the internal secret AND a per-op, path-scoped RS256
56
+ // token carrying op:'delete' (owner ruling #10, defense in depth). Sign+send both.
57
+ const token = await cfg.signFileToken({ k: appKey(key), op: 'delete' });
58
+ const res = await fetch(
59
+ `${cfg.baseUrl}/media/${appKey(key)}?token=${encodeURIComponent(token)}`,
60
+ {
61
+ method: 'DELETE',
62
+ headers: cfg.internalSecret ? { 'x-internal-secret': cfg.internalSecret } : {},
63
+ },
64
+ );
65
+ if (!res.ok && res.status !== 404)
66
+ throw new Error(`binary-server remove ${key}: ${res.status}`);
67
+ },
68
+ async removePrefix(prefix) {
69
+ // The delete token's exact-key claim is the container path; bs derives the tree from it
70
+ // plus ?container=1. Same two-barrier gate as remove().
71
+ const token = await cfg.signFileToken({ k: appKey(prefix), op: 'delete' });
72
+ const res = await fetch(
73
+ `${cfg.baseUrl}/media/${appKey(prefix)}?container=1&token=${encodeURIComponent(token)}`,
74
+ {
75
+ method: 'DELETE',
76
+ headers: cfg.internalSecret ? { 'x-internal-secret': cfg.internalSecret } : {},
77
+ },
78
+ );
79
+ if (!res.ok && res.status !== 404) {
80
+ throw new Error(`binary-server removePrefix ${prefix}: ${res.status}`);
81
+ }
82
+ },
83
+ async stat(key) {
84
+ // A 0-0 range is the cheapest way to learn total size (Content-Range: …/<total>).
85
+ const res = await fetch(await mediaUrl(key), { headers: { Range: 'bytes=0-0' } });
86
+ if (res.status === 404) return null;
87
+ const cr = res.headers.get('content-range');
88
+ const total = cr ? Number(cr.split('/')[1]) : Number(res.headers.get('content-length'));
89
+ return Number.isFinite(total) ? { size: total } : null;
90
+ },
91
+ async getRange(key, range) {
92
+ const headers: Record<string, string> = {};
93
+ if (range) headers.Range = `bytes=${range.start}-${range.end}`;
94
+ const res = await fetch(await mediaUrl(key), { headers });
95
+ if (res.status === 404 || !res.body) return null;
96
+ return { stream: res.body };
97
+ },
98
+ async redirectUrl(key, opts) {
99
+ return mediaUrl(key, { fn: opts.filename, dl: 'inline' });
100
+ },
101
+ };
102
+ }