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,239 @@
1
+ /**
2
+ * `createS3ObjectStore` — the SAAS profile's binary tier (08 §1b, 01 §2.5).
3
+ *
4
+ * The companion to `createFsObjectStore` (cwip): same `ObjectStore` contract,
5
+ * different issuer. Everything above this line — `AssetService`, the asset
6
+ * mixin, the client — is written against the interface, so switching profiles
7
+ * swaps this factory in and changes nothing else. That is the whole reason the
8
+ * local store bothers to sign its URLs.
9
+ *
10
+ * Built on `Bun.S3Client`, which ships in the runtime cursedbelt already
11
+ * requires — no `aws-sdk`, no transitive credential-chain surprises. Works
12
+ * against S3, R2, MinIO or any other S3-compatible endpoint.
13
+ *
14
+ * Three things this does that a naive `PutObject` wrapper does not:
15
+ *
16
+ * * **Streams through a multipart writer.** A 4GB upload is fed part-by-part
17
+ * (default 8MiB) straight from the request stream. Nothing buffers the
18
+ * object, so request memory is O(part), not O(file).
19
+ * * **Aborts the multipart on failure.** `NetworkSink.end(error)` tells Bun to
20
+ * abort rather than complete the upload; an abandoned multipart otherwise
21
+ * accrues storage cost that never appears in a bucket listing — an invisible
22
+ * bill, which is the worst kind.
23
+ * * **Presigns for real.** `signedUrl` returns a genuine presigned GET so the
24
+ * bytes leave via the CDN/edge, not through the app process. The asset
25
+ * mixin's bytes routes stay as the fallback for private/derived content.
26
+ *
27
+ * Credentials are OPTIONS, never `process.env`: the host resolves them from
28
+ * GuardEngine secrets or its deploy environment (01 §3.3) and injects them, so
29
+ * this module has no ambient authority and a test can hand it a fake client.
30
+ */
31
+ import { assertObjectKey } from 'cwip/storage';
32
+ import type { ObjectStat, ObjectStore } from 'cwip/storage';
33
+ import { AppError } from '../errors';
34
+
35
+ /**
36
+ * The slice of `Bun.S3File` this store uses.
37
+ *
38
+ * Declared structurally rather than imported as `Bun.S3File` so the contract
39
+ * suite can drive BOTH implementations against an in-process fake bucket. A
40
+ * store whose only proof is "it compiles against the real SDK types" is a store
41
+ * whose behavior has never been tested.
42
+ */
43
+ export interface S3ObjectHandle {
44
+ stream(): ReadableStream<Uint8Array>;
45
+ writer(options?: { partSize?: number; queueSize?: number; retry?: number }): S3ObjectWriter;
46
+ exists(): Promise<boolean>;
47
+ stat(): Promise<{ size: number; type?: string; lastModified?: Date; etag?: string }>;
48
+ delete(): Promise<void>;
49
+ presign(options?: { expiresIn?: number; method?: 'GET' }): string;
50
+ }
51
+
52
+ export interface S3ObjectWriter {
53
+ write(chunk: Uint8Array): number | Promise<number>;
54
+ /** Passing an error aborts the multipart upload instead of completing it. */
55
+ end(error?: Error): number | Promise<number>;
56
+ }
57
+
58
+ export interface S3ClientLike {
59
+ file(key: string, options?: { type?: string }): S3ObjectHandle;
60
+ }
61
+
62
+ export interface S3ObjectStoreOptions {
63
+ /** Pre-built client. Supply this OR the credential fields below. */
64
+ client?: S3ClientLike;
65
+ bucket?: string;
66
+ /** e.g. `https://<account>.r2.cloudflarestorage.com`. */
67
+ endpoint?: string;
68
+ region?: string;
69
+ accessKeyId?: string;
70
+ secretAccessKey?: string;
71
+ sessionToken?: string;
72
+ /**
73
+ * Namespace every key under this prefix, so ONE bucket can host many apps
74
+ * without collisions. Tenant scoping stays in the key shape (`<tenant>/<file>`)
75
+ * — the prefix is deployment topology, not a security boundary.
76
+ */
77
+ keyPrefix?: string;
78
+ /** Multipart part size. Default 8MiB. */
79
+ partSize?: number;
80
+ /** Default presigned-URL lifetime in seconds. */
81
+ defaultExpiresInSeconds?: number;
82
+ /** Ceiling on a caller-requested lifetime. */
83
+ maxExpiresInSeconds?: number;
84
+ }
85
+
86
+ const DEFAULT_PART_SIZE = 8 * 1024 * 1024;
87
+ const DEFAULT_EXPIRES_IN_SECONDS = 300;
88
+ const MAX_EXPIRES_IN_SECONDS = 86_400;
89
+
90
+ /** `Bun.S3Client` typed down to the slice we use; Bun ships it natively. */
91
+ type BunWithS3 = { S3Client: new (options: Record<string, unknown>) => S3ClientLike };
92
+
93
+ const buildClient = (options: S3ObjectStoreOptions): S3ClientLike => {
94
+ if (options.client) return options.client;
95
+ if (!options.bucket)
96
+ throw new AppError('createS3ObjectStore needs either a `client` or a `bucket` (plus credentials).', {
97
+ code: 'OBJECT_STORE_MISCONFIGURED',
98
+ status: 500,
99
+ category: 'storage',
100
+ isOperational: false,
101
+ });
102
+ const S3Client = (Bun as unknown as BunWithS3).S3Client;
103
+ if (typeof S3Client !== 'function')
104
+ throw new AppError('This Bun runtime has no S3Client; pass a `client` implementing the S3 subset instead.', {
105
+ code: 'OBJECT_STORE_MISCONFIGURED',
106
+ status: 500,
107
+ category: 'storage',
108
+ isOperational: false,
109
+ });
110
+ return new S3Client({
111
+ bucket: options.bucket,
112
+ endpoint: options.endpoint,
113
+ region: options.region,
114
+ accessKeyId: options.accessKeyId,
115
+ secretAccessKey: options.secretAccessKey,
116
+ sessionToken: options.sessionToken,
117
+ });
118
+ };
119
+
120
+ export function createS3ObjectStore(options: S3ObjectStoreOptions): ObjectStore {
121
+ const client = buildClient(options);
122
+ const partSize = options.partSize ?? DEFAULT_PART_SIZE;
123
+ const defaultExpiry = options.defaultExpiresInSeconds ?? DEFAULT_EXPIRES_IN_SECONDS;
124
+ const maxExpiry = options.maxExpiresInSeconds ?? MAX_EXPIRES_IN_SECONDS;
125
+ const prefix = options.keyPrefix ? `${options.keyPrefix.replace(/^\/+|\/+$/g, '')}/` : '';
126
+
127
+ /**
128
+ * The SAME key whitelist the fs store enforces.
129
+ *
130
+ * S3 has no directories, so traversal is not the risk here — divergence is. A
131
+ * key that one profile accepts and the other refuses turns a profile switch
132
+ * into a data-loss migration, so both stores validate identically and the
133
+ * contract suite asserts it.
134
+ */
135
+ const physical = (key: string): string => {
136
+ assertObjectKey(key);
137
+ return `${prefix}${key}`;
138
+ };
139
+
140
+ const statOf = async (key: string): Promise<ObjectStat | null> => {
141
+ const handle = client.file(physical(key));
142
+ try {
143
+ const info = await handle.stat();
144
+ return {
145
+ key,
146
+ size: info.size,
147
+ contentType: info.type,
148
+ modifiedAt: (info.lastModified ?? new Date()).toISOString(),
149
+ };
150
+ } catch (error) {
151
+ if (isNotFound(error)) return null;
152
+ throw error;
153
+ }
154
+ };
155
+
156
+ return {
157
+ async put(key, body, putOptions): Promise<ObjectStat> {
158
+ const contentType = putOptions?.contentType;
159
+ const handle = client.file(physical(key), contentType ? { type: contentType } : undefined);
160
+ const writer = handle.writer({ partSize });
161
+ const reader = body.getReader();
162
+ let size = 0;
163
+ try {
164
+ while (true) {
165
+ const { done, value } = await reader.read();
166
+ if (done) break;
167
+ if (value.byteLength === 0) continue;
168
+ await writer.write(value);
169
+ size += value.byteLength;
170
+ }
171
+ await writer.end();
172
+ } catch (error) {
173
+ // Abort, don't complete. Best-effort: if the abort itself fails there is
174
+ // nothing further this process can do, and swallowing it would mask the
175
+ // ORIGINAL upload failure, which is the one worth reporting.
176
+ await Promise.resolve(writer.end(error instanceof Error ? error : new Error(String(error)))).catch(() => {});
177
+ throw error;
178
+ } finally {
179
+ reader.releaseLock();
180
+ }
181
+ return { key, size, contentType, modifiedAt: new Date().toISOString() };
182
+ },
183
+
184
+ async get(key): Promise<ReadableStream<Uint8Array> | null> {
185
+ const handle = client.file(physical(key));
186
+ if (!(await handle.exists())) return null;
187
+ try {
188
+ return handle.stream();
189
+ } catch (error) {
190
+ // Raced with a delete between `exists` and `stream` — a missing object
191
+ // is `null`, not a 500.
192
+ if (isNotFound(error)) return null;
193
+ throw error;
194
+ }
195
+ },
196
+
197
+ stat: statOf,
198
+
199
+ async delete(key): Promise<boolean> {
200
+ const existed = (await statOf(key)) !== null;
201
+ try {
202
+ await client.file(physical(key)).delete();
203
+ } catch (error) {
204
+ if (!isNotFound(error)) throw error;
205
+ }
206
+ return existed;
207
+ },
208
+
209
+ async signedUrl(key, urlOptions): Promise<string> {
210
+ const requested = urlOptions?.expiresInSeconds ?? defaultExpiry;
211
+ if (!Number.isFinite(requested) || requested <= 0)
212
+ throw new AppError(`Signed-url lifetime for '${key}' must be a positive number of seconds.`, {
213
+ code: 'OBJECT_KEY_INVALID',
214
+ status: 400,
215
+ category: 'storage',
216
+ context: { key, requested },
217
+ });
218
+ return client
219
+ .file(physical(key))
220
+ .presign({ expiresIn: Math.min(Math.floor(requested), maxExpiry), method: 'GET' });
221
+ },
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Is this the store telling us the object is not there?
227
+ *
228
+ * S3-compatible endpoints disagree on the shape (`NoSuchKey`, `NotFound`, a
229
+ * bare 404), and Bun surfaces its own `code`. Matching all of them keeps a
230
+ * missing object a `null` on every backend rather than a 500 on MinIO and a
231
+ * `null` on R2.
232
+ */
233
+ const isNotFound = (error: unknown): boolean => {
234
+ if (!error || typeof error !== 'object') return false;
235
+ const candidate = error as { code?: unknown; name?: unknown; status?: unknown; statusCode?: unknown };
236
+ const code = String(candidate.code ?? candidate.name ?? '');
237
+ if (/NoSuchKey|NotFound|ENOENT|ERR_S3_FILE_NOT_FOUND/i.test(code)) return true;
238
+ return candidate.status === 404 || candidate.statusCode === 404;
239
+ };
@@ -0,0 +1,245 @@
1
+ import type { PlayableVideo, ProcessingStatus } from 'cursedbelt-core/media';
2
+ import type { StreamTokenService } from './streamToken';
3
+ import {
4
+ fsMp4Key,
5
+ fsPosterKey,
6
+ fsSourceKey,
7
+ fsSpriteKey,
8
+ fsVttKey,
9
+ type MediaCtx,
10
+ type MediaSource,
11
+ type MediaStore,
12
+ } from './types';
13
+ import { processVideo } from './videoProcessor';
14
+
15
+ // ── Media URL builder ───────────────────────────────────────────────────────────
16
+ // Builds the path-embedded-token URLs the serve routes expect. One token per video authorizes
17
+ // all its derivative URLs; carried as a leading `/s/<token>/` path segment so hls.js's relative
18
+ // segment/playlist fetches inherit it (see streamToken.ts).
19
+ //
20
+ // SPLIT-ORIGIN: these URLs are built SERVER-SIDE and baked into the PlayableVideo the client
21
+ // receives — the browser never derives a media URL from `window.location`. So `baseUrl` may be
22
+ // ABSOLUTE (e.g. 'https://api.example.com/_media') to point media at an API subdomain / EC2 /
23
+ // binary-server edge that differs from where the SPA is served (Cloudflare Pages). It is
24
+ // intentionally SEPARATE from `mountMediaRoutes`' `basePath` (the on-server route mount, always
25
+ // path-only): the two only need to share the same trailing path (e.g. '/_media'). For a
26
+ // same-origin app, pass the plain path ('/_media'). When absolute (cross-origin), the media
27
+ // routes must send CORS headers — pass `cors` to `mountMediaRoutes`.
28
+ export interface MediaUrls {
29
+ build(
30
+ ctx: MediaCtx,
31
+ sourceId: string,
32
+ videoId: string,
33
+ ): Promise<{
34
+ streamUrl: string;
35
+ hlsUrl: string;
36
+ posterUrl: string;
37
+ displayUrl: string;
38
+ vttUrl: string;
39
+ spriteUrl: string;
40
+ }>;
41
+ }
42
+
43
+ export function createMediaUrls(opts: {
44
+ /** Public base for browser media URLs. Path-only ('/_media') for same-origin, or an absolute
45
+ * origin+path ('https://api.example.com/_media') for a split-origin deploy. Must share its
46
+ * trailing path with `mountMediaRoutes`' `basePath`. */
47
+ baseUrl: string;
48
+ token: StreamTokenService;
49
+ /** Owner keyspace segment for a request. Defaults to tenantId (a tenant-shared gallery). */
50
+ owner?: (ctx: MediaCtx) => string;
51
+ }): MediaUrls {
52
+ const ownerOf = opts.owner ?? ((c: MediaCtx) => c.tenantId);
53
+ // Trim a trailing slash so 'https://api…/_media/' and '/_media' behave identically.
54
+ const base = opts.baseUrl.replace(/\/$/, '');
55
+ return {
56
+ async build(ctx, sourceId, videoId) {
57
+ const token = await opts.token.sign({ owner: ownerOf(ctx), sourceId, videoId });
58
+ const p = `${base}/s/${token}`;
59
+ return {
60
+ streamUrl: `${p}/media/${sourceId}/${videoId}`,
61
+ hlsUrl: `${p}/hls/${sourceId}/${videoId}/master.m3u8`,
62
+ posterUrl: `${p}/poster/${sourceId}/${videoId}`,
63
+ displayUrl: `${p}/display/${sourceId}/${videoId}`,
64
+ vttUrl: `${p}/sprite/${sourceId}/${videoId}/sprites.vtt`,
65
+ spriteUrl: `${p}/sprite/${sourceId}/${videoId}/sprites.jpg`,
66
+ };
67
+ },
68
+ };
69
+ }
70
+
71
+ // ── Source registry ───────────────────────────────────────────────────────────────
72
+ export class SourceRegistry {
73
+ private sources = new Map<string, MediaSource>();
74
+ register(source: MediaSource): this {
75
+ this.sources.set(source.id, source);
76
+ return this;
77
+ }
78
+ get(id: string): MediaSource | undefined {
79
+ return this.sources.get(id);
80
+ }
81
+ list(): MediaSource[] {
82
+ return [...this.sources.values()];
83
+ }
84
+ }
85
+
86
+ // ── Disk-backed source ──────────────────────────────────────────────────────────
87
+ // A MediaSource over a MediaStore: ingests uploads, runs the pipeline in the background, and
88
+ // resolves videos to tokenised PlayableVideos. Metadata is held in memory keyed by tenant (an
89
+ // app that needs persistence swaps this for a DB-backed MediaSource — cursedbelt-server/sqlite
90
+ // — without touching consumers).
91
+ type VideoMeta = {
92
+ id: string;
93
+ name: string;
94
+ tags: string[];
95
+ mime: string;
96
+ durationSeconds?: number;
97
+ status: ProcessingStatus;
98
+ progress: number;
99
+ hasHls: boolean;
100
+ hasSprite: boolean;
101
+ hasPoster: boolean;
102
+ createdAt: number;
103
+ };
104
+
105
+ export type DiskSource = MediaSource & {
106
+ /** Read current processing status (for the gallery's status poll / tests). */
107
+ status(ctx: MediaCtx, videoId: string): { status: ProcessingStatus; progress: number } | null;
108
+ };
109
+
110
+ export function createDiskSource(cfg: {
111
+ id: string;
112
+ title: string;
113
+ store: MediaStore;
114
+ /** ffmpeg scratch dir. */
115
+ workDir: string;
116
+ urls: MediaUrls;
117
+ /** Owner keyspace segment; defaults to tenantId. */
118
+ owner?: (ctx: MediaCtx) => string;
119
+ /** Injectable clock (tests). */
120
+ now?: () => number;
121
+ }): DiskSource {
122
+ const ownerOf = cfg.owner ?? ((c: MediaCtx) => c.tenantId);
123
+ const now = cfg.now ?? (() => Date.now());
124
+ const byTenant = new Map<string, Map<string, VideoMeta>>();
125
+ const bucket = (ctx: MediaCtx) => {
126
+ let m = byTenant.get(ctx.tenantId);
127
+ if (!m) {
128
+ m = new Map();
129
+ byTenant.set(ctx.tenantId, m);
130
+ }
131
+ return m;
132
+ };
133
+
134
+ const toPlayable = async (ctx: MediaCtx, meta: VideoMeta): Promise<PlayableVideo> => {
135
+ const u = await cfg.urls.build(ctx, cfg.id, meta.id);
136
+ return {
137
+ id: meta.id,
138
+ name: meta.name,
139
+ streamUrl: u.streamUrl,
140
+ hlsUrl: meta.hasHls ? u.hlsUrl : undefined,
141
+ posterUrl: meta.hasPoster ? u.posterUrl : undefined,
142
+ vttUrl: meta.hasSprite ? u.vttUrl : undefined,
143
+ spriteUrl: meta.hasSprite ? u.spriteUrl : undefined,
144
+ durationSeconds: meta.durationSeconds,
145
+ tags: meta.tags,
146
+ processingStatus: meta.status,
147
+ processingProgress: meta.progress,
148
+ };
149
+ };
150
+
151
+ return {
152
+ id: cfg.id,
153
+ title: cfg.title,
154
+
155
+ async list(ctx, opts) {
156
+ const all = [...bucket(ctx).values()].sort((a, b) => b.createdAt - a.createdAt);
157
+ const q = opts.query?.trim().toLowerCase();
158
+ const filtered = all.filter(
159
+ (m) =>
160
+ (!opts.tag || m.tags.includes(opts.tag)) &&
161
+ (!q || m.name.toLowerCase().includes(q) || m.tags.some((t) => t.includes(q))),
162
+ );
163
+ const limit = opts.limit ?? 24;
164
+ const start = opts.cursor ? Number(opts.cursor) || 0 : 0;
165
+ const page = filtered.slice(start, start + limit);
166
+ const items = await Promise.all(page.map((m) => toPlayable(ctx, m)));
167
+ const next = start + limit;
168
+ return { items, nextCursor: next < filtered.length ? String(next) : undefined };
169
+ },
170
+
171
+ async resolve(ctx, videoId) {
172
+ const meta = bucket(ctx).get(videoId);
173
+ return meta ? toPlayable(ctx, meta) : null;
174
+ },
175
+
176
+ async tags(ctx) {
177
+ const set = new Set<string>();
178
+ for (const m of bucket(ctx).values()) {
179
+ for (const t of m.tags) set.add(t);
180
+ }
181
+ return [...set].sort();
182
+ },
183
+
184
+ async ingest(ctx, upload) {
185
+ const owner = ownerOf(ctx);
186
+ const id = crypto.randomUUID();
187
+ await cfg.store.putStream(fsSourceKey(owner, id), upload.body, upload.mime, {
188
+ contentLength: upload.size,
189
+ });
190
+ const meta: VideoMeta = {
191
+ id,
192
+ name: upload.name,
193
+ tags: [],
194
+ mime: upload.mime,
195
+ status: 'processing',
196
+ progress: 0,
197
+ hasHls: false,
198
+ hasSprite: false,
199
+ hasPoster: false,
200
+ createdAt: now(),
201
+ };
202
+ bucket(ctx).set(id, meta);
203
+
204
+ // Fire-and-forget the pipeline; the client polls status via the source's status()/list().
205
+ void processVideo({
206
+ store: cfg.store,
207
+ workDir: cfg.workDir,
208
+ owner,
209
+ id,
210
+ sourceKey: fsSourceKey(owner, id),
211
+ onProgress: (status, pct) => {
212
+ meta.status = status;
213
+ meta.progress = pct;
214
+ },
215
+ }).then((res) => {
216
+ meta.status = res.status;
217
+ meta.progress = res.status === 'ready' ? 100 : meta.progress;
218
+ meta.durationSeconds = res.durationSeconds || meta.durationSeconds;
219
+ meta.hasHls = res.hasHls;
220
+ meta.hasSprite = res.hasSprite;
221
+ meta.hasPoster = res.hasPoster;
222
+ });
223
+
224
+ return toPlayable(ctx, meta);
225
+ },
226
+
227
+ async remove(ctx, videoId) {
228
+ const owner = ownerOf(ctx);
229
+ await Promise.all([
230
+ cfg.store.remove(fsSourceKey(owner, videoId)),
231
+ cfg.store.remove(fsMp4Key(owner, videoId)),
232
+ cfg.store.remove(fsPosterKey(owner, videoId)),
233
+ cfg.store.remove(fsSpriteKey(owner, videoId)),
234
+ cfg.store.remove(fsVttKey(owner, videoId)),
235
+ cfg.store.removePrefix(`hls/${owner}/${videoId}`),
236
+ ]);
237
+ bucket(ctx).delete(videoId);
238
+ },
239
+
240
+ status(ctx, videoId) {
241
+ const meta = bucket(ctx).get(videoId);
242
+ return meta ? { status: meta.status, progress: meta.progress } : null;
243
+ },
244
+ };
245
+ }