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,84 @@
1
+ import { clampPageLimit } from 'cwip/math';
2
+ import { isReadOnlySql } from './readonlySql';
3
+ /**
4
+ * Hono-free read-only SQLite browse primitives — the shared core beneath
5
+ * `createSqliteViewerMiddleware` (viewer.ts) AND any consumer that can't mount a
6
+ * Hono sub-app (e.g. a cursedshell plugin's `defineService`, whose routes are pure
7
+ * data, or an app with a bespoke transport). One primitive per concern: the
8
+ * middleware is now a thin HTTP wrapper over these functions.
9
+ *
10
+ * Every path is read-only and injection-safe: table names are validated against
11
+ * `sqlite_master` before interpolation, all values are bound, and ad-hoc SQL is
12
+ * gated by {@link isReadOnlySql} (SELECT/WITH only). Authn/authz is the caller's job.
13
+ */
14
+ /** A sane admin page-size window. */
15
+ const MAX_PAGE = 1000;
16
+ /** Clamp a requested page size into `[1, MAX_PAGE]`, falling back when unparseable. */
17
+ export function clampLimit(raw, fallback) {
18
+ return clampPageLimit(raw, { max: MAX_PAGE, fallback });
19
+ }
20
+ /** Clamp an offset to a non-negative integer (0 when unparseable). */
21
+ export function clampOffset(raw) {
22
+ return clampPageLimit(raw, { min: 0, max: Number.POSITIVE_INFINITY, fallback: 0 });
23
+ }
24
+ /** Is `name` an actual table in this DB? (The injection guard for `SELECT * FROM`.) */
25
+ export function isKnownTable(db, name) {
26
+ const row = db
27
+ .query("SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name = ?")
28
+ .get(name);
29
+ return (row?.n ?? 0) > 0;
30
+ }
31
+ /** Double-quote a name already proven to exist in `sqlite_master`. */
32
+ export function quoteIdent(name) {
33
+ return `"${name.replace(/"/g, '""')}"`;
34
+ }
35
+ /** User tables (FTS shadow + `sqlite_*` internals excluded) with row counts. */
36
+ export function listSqliteTables(db) {
37
+ const names = db
38
+ .query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name")
39
+ .all()
40
+ .map((r) => r.name);
41
+ return names.map((name) => ({
42
+ name,
43
+ count: db.query(`SELECT COUNT(*) AS n FROM ${quoteIdent(name)}`).get()?.n ?? 0,
44
+ }));
45
+ }
46
+ export class UnknownTableError extends Error {
47
+ table;
48
+ constructor(table) {
49
+ super(`Unknown table: ${table}`);
50
+ this.table = table;
51
+ this.name = 'UnknownTableError';
52
+ }
53
+ }
54
+ /**
55
+ * A clamped, paginated window over one table. Throws {@link UnknownTableError} for a
56
+ * table that isn't in `sqlite_master` (callers map that to a 404).
57
+ */
58
+ export function readSqliteTable(db, table, opts = {}) {
59
+ if (!isKnownTable(db, table))
60
+ throw new UnknownTableError(table);
61
+ const limit = clampLimit(opts.limit, 50);
62
+ const offset = clampOffset(opts.offset);
63
+ const items = db
64
+ .query(`SELECT * FROM ${quoteIdent(table)} LIMIT ? OFFSET ?`)
65
+ .all(limit, offset);
66
+ const total = db.query(`SELECT COUNT(*) AS n FROM ${quoteIdent(table)}`).get()?.n ?? 0;
67
+ return { items, total, limit, offset };
68
+ }
69
+ export class NotReadOnlyError extends Error {
70
+ constructor() {
71
+ super('Only read-only SELECT/WITH queries are allowed');
72
+ this.name = 'NotReadOnlyError';
73
+ }
74
+ }
75
+ /**
76
+ * Run an ad-hoc read-only query with bound params. Throws {@link NotReadOnlyError}
77
+ * for anything the {@link isReadOnlySql} guard rejects (non-SELECT, stacked
78
+ * statements, PRAGMA, …); SQLite errors (bad column/table) propagate as `Error`.
79
+ */
80
+ export function runReadonlySqlite(db, sql, params = []) {
81
+ if (typeof sql !== 'string' || !isReadOnlySql(sql))
82
+ throw new NotReadOnlyError();
83
+ return db.query(sql).all(...params);
84
+ }
@@ -0,0 +1,32 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Export primitives for `bun:sqlite` — the missing "get the data out" half of the
4
+ * SQLite toolkit. Table export composes the ONE canonical serializers (`toCsv` from
5
+ * `cwip/csv`, `JSON.stringify` for JSON — no third CSV/JSON path); snapshot export
6
+ * reuses the `VACUUM INTO` primitive (see `backup.ts`) to produce a portable `.sqlite`.
7
+ *
8
+ * These return in-memory payloads (text / bytes) rather than streaming to a Response,
9
+ * so they're transport-agnostic: a cursedshell plugin returns them over its JSON RPC
10
+ * (base64 for bytes), a Hono app streams them as a `Content-Disposition: attachment`
11
+ * download. Admin-scale — a whole-DB snapshot is base64'd through JSON, fine for
12
+ * typical DB sizes; very large DBs should stream a file route instead.
13
+ */
14
+ export type TableExportFormat = 'csv' | 'json';
15
+ export interface TableExport {
16
+ filename: string;
17
+ mime: string;
18
+ text: string;
19
+ }
20
+ /** All rows of one table serialized to CSV or JSON. Throws {@link UnknownTableError}. */
21
+ export declare function exportSqliteTable(db: Database, table: string, format: TableExportFormat): TableExport;
22
+ export interface SnapshotExport {
23
+ filename: string;
24
+ mime: string;
25
+ bytes: Uint8Array;
26
+ }
27
+ /**
28
+ * A compact, WAL-merged copy of the whole database as `.sqlite` bytes, via
29
+ * `VACUUM INTO` a throwaway temp file (the same primitive `createSqliteBackupService`
30
+ * uses). The temp file is always removed.
31
+ */
32
+ export declare function exportSqliteSnapshot(db: Database): SnapshotExport;
@@ -0,0 +1,42 @@
1
+ import { readFileSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { toCsv } from 'cwip/csv';
5
+ import { isKnownTable, quoteIdent, UnknownTableError } from './browse';
6
+ /** All rows of one table serialized to CSV or JSON. Throws {@link UnknownTableError}. */
7
+ export function exportSqliteTable(db, table, format) {
8
+ if (!isKnownTable(db, table))
9
+ throw new UnknownTableError(table);
10
+ const rows = db.query(`SELECT * FROM ${quoteIdent(table)}`).all();
11
+ if (format === 'json') {
12
+ return {
13
+ filename: `${table}.json`,
14
+ mime: 'application/json',
15
+ text: JSON.stringify(rows, null, 2),
16
+ };
17
+ }
18
+ return {
19
+ filename: `${table}.csv`,
20
+ mime: 'text/csv',
21
+ // toCsv needs at least the columns; an empty table yields an empty string, which
22
+ // is a valid (header-less) CSV for a zero-row export.
23
+ text: toCsv(rows),
24
+ };
25
+ }
26
+ /**
27
+ * A compact, WAL-merged copy of the whole database as `.sqlite` bytes, via
28
+ * `VACUUM INTO` a throwaway temp file (the same primitive `createSqliteBackupService`
29
+ * uses). The temp file is always removed.
30
+ */
31
+ export function exportSqliteSnapshot(db) {
32
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
33
+ const tmp = join(tmpdir(), `cb-export-${stamp}-${Math.random().toString(36).slice(2)}.sqlite`);
34
+ try {
35
+ db.run('VACUUM INTO ?', [tmp]);
36
+ const bytes = new Uint8Array(readFileSync(tmp));
37
+ return { filename: `snapshot-${stamp}.sqlite`, mime: 'application/x-sqlite3', bytes };
38
+ }
39
+ finally {
40
+ rmSync(tmp, { force: true });
41
+ }
42
+ }
@@ -0,0 +1,106 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ export interface FtsTableSpec {
3
+ /** The FTS5 virtual table to create, e.g. `'notes_fts'`. */
4
+ table: string;
5
+ /** The source ("content") table whose text it indexes, e.g. `'notes'`. */
6
+ contentTable: string;
7
+ /** The content table's rowid/primary-key column. Default: `'id'`. */
8
+ contentRowid?: string;
9
+ /** Indexed columns — must exist on the content table, in this order. */
10
+ columns: string[];
11
+ /**
12
+ * FTS5 tokenizer directive. Default: `unicode61 remove_diacritics 2` (case- and
13
+ * accent-folding, the right default for human text). Pass e.g. `'porter unicode61'`
14
+ * for stemming, or `'trigram'` for substring search.
15
+ */
16
+ tokenize?: string;
17
+ }
18
+ /**
19
+ * Build the DDL for one external-content FTS5 table and its three sync triggers
20
+ * (insert/update/delete), as a single multi-statement string ready for `db.run()`.
21
+ *
22
+ * External content (`content=<table>, content_rowid=<pk>`) means the FTS index stores
23
+ * only the inverted index, never a second copy of the text — the triggers mirror every
24
+ * write into the index. This is the canonical pattern from the SQLite FTS5 docs,
25
+ * generated once so apps don't repeat it per table.
26
+ */
27
+ export declare function buildFtsSchema(spec: FtsTableSpec): string;
28
+ /** Create every table in `specs` and its triggers on `db` (one `db.run` per spec). */
29
+ export declare function applyFtsSchema(db: Database, ...specs: FtsTableSpec[]): void;
30
+ export interface FtsResult {
31
+ /** The content row's rowid (its `content_rowid` value). */
32
+ rowid: number;
33
+ /** FTS5 bm25 relevance — lower (more negative) is more relevant. */
34
+ rank: number;
35
+ /** Present only when `snippet` was requested: a truncated, match-marked excerpt. */
36
+ snippet?: string;
37
+ /** Present only when `highlight` was requested: the full column with matches marked. */
38
+ highlight?: string;
39
+ }
40
+ export interface FtsSnippetOpts {
41
+ /** FTS column index to excerpt; `-1` (default) auto-picks the best-matching column. */
42
+ column?: number;
43
+ /** Wraps each match, default `'<b>'`. */
44
+ startMatch?: string;
45
+ /** Closes each match, default `'</b>'`. */
46
+ endMatch?: string;
47
+ /** Inserted where text is trimmed, default `'…'`. */
48
+ ellipsis?: string;
49
+ /** Max tokens in the excerpt (1–64), default `16`. */
50
+ maxTokens?: number;
51
+ }
52
+ export interface FtsHighlightOpts {
53
+ /** FTS column index to return (required — `highlight()` has no auto column). */
54
+ column: number;
55
+ /** Wraps each match, default `'<b>'`. */
56
+ startMatch?: string;
57
+ /** Closes each match, default `'</b>'`. */
58
+ endMatch?: string;
59
+ }
60
+ export interface FtsSearchOpts {
61
+ /** The FTS5 table to query, e.g. `'notes_fts'`. */
62
+ table: string;
63
+ /** Raw user query — sanitized into a prefix-match expression before MATCH. */
64
+ query: string;
65
+ /** Page size, default `20`. */
66
+ limit?: number;
67
+ /** Row offset, default `0`. */
68
+ offset?: number;
69
+ /** Request a `snippet(...)` excerpt column. */
70
+ snippet?: FtsSnippetOpts;
71
+ /** Request a `highlight(...)` column. */
72
+ highlight?: FtsHighlightOpts;
73
+ }
74
+ export interface Fts5Search {
75
+ /** Ranked search; returns `[]` for an empty/operator-only query (never throws on it). */
76
+ search(opts: FtsSearchOpts): FtsResult[];
77
+ /** Rebuild one FTS index from its content table (post-ETL / after `clear`). */
78
+ rebuild(table: string): void;
79
+ /** Rebuild every table passed to {@link createFts5Search} (`tables` option). */
80
+ rebuildAll(): void;
81
+ /** Empty an FTS index (the `'delete-all'` command) — pair with `rebuild` for bulk loads. */
82
+ clear(table: string): void;
83
+ /** Merge the index's b-tree segments (the `'optimize'` command) for faster queries. */
84
+ optimize(table: string): void;
85
+ }
86
+ /**
87
+ * Build a ranked-search facade over the FTS5 tables named in `opts.tables` (the set
88
+ * `rebuildAll()` iterates). The `db` handle is the same one the app uses — one
89
+ * connection, one WAL.
90
+ */
91
+ export declare function createFts5Search(db: Database, opts?: {
92
+ tables?: string[];
93
+ }): Fts5Search;
94
+ /**
95
+ * Convert arbitrary user input into a safe FTS5 MATCH expression: every run of
96
+ * letters/digits/underscore becomes a quoted prefix token (`"term"*`), joined by
97
+ * spaces (FTS5's implicit AND). All FTS5 operators (`" * : ^ ( ) - + NEAR …`) are
98
+ * dropped with the surrounding punctuation, so the query can neither inject syntax
99
+ * nor raise a parse error. Returns `''` when nothing searchable remains.
100
+ *
101
+ * Examples: `hello` → `"hello"*`; `hello world` → `"hello"* "world"*` (both must
102
+ * match); `DROP TABLE` → `"DROP"* "TABLE"*` (a literal search, harmless).
103
+ */
104
+ export declare function sanitizeFtsQuery(raw: string, opts?: {
105
+ mode?: 'all' | 'any';
106
+ }): string;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * FTS5 full-text search for `bun:sqlite` — the from-scratch replacement for the apps'
3
+ * hand-rolled `LIKE '%' || ? || '%'` search. Three pieces:
4
+ *
5
+ * 1. {@link buildFtsSchema} — generates the `CREATE VIRTUAL TABLE` + the three
6
+ * external-content sync triggers for one indexed table. Apps call it from a
7
+ * migration instead of hand-writing the (highly repetitive) trigger SQL 4×.
8
+ * 2. {@link createFts5Search} — a ranked query builder (bm25 `rank`, `snippet`,
9
+ * `highlight`) over those tables, plus the FTS5 maintenance commands
10
+ * (`rebuild`/`clear`/`optimize`) the ETL and bulk importers need.
11
+ * 3. {@link sanitizeFtsQuery} — turns arbitrary user input into a safe AND-of-prefix
12
+ * FTS5 MATCH expression (no operator injection, no parse errors).
13
+ *
14
+ * FTS5 is compiled into `bun:sqlite` (NOT `node:sqlite`, which omits it). Everything
15
+ * here is app-agnostic: the indexed table names are a *constructor option*, never
16
+ * hard-coded — ca and ru index different tables.
17
+ */
18
+ /** A safe, unquoted SQL identifier (table/column name). */
19
+ const IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
20
+ function assertIdent(name, what) {
21
+ if (!IDENT.test(name)) {
22
+ throw new Error(`Invalid ${what}: ${JSON.stringify(name)} (expected a plain SQL identifier)`);
23
+ }
24
+ return name;
25
+ }
26
+ /** Double-quote an identifier (validated first, so the quoting can't be escaped out of). */
27
+ const q = (name) => `"${name}"`;
28
+ /** Single-quote a string literal for inlining (doubles embedded quotes). */
29
+ const lit = (s) => `'${s.replace(/'/g, "''")}'`;
30
+ /** Coerce to a bounded non-negative integer for inlining (snippet token counts, etc.). */
31
+ const int = (n, fallback) => Number.isFinite(n) ? Math.trunc(n) : fallback;
32
+ /**
33
+ * Build the DDL for one external-content FTS5 table and its three sync triggers
34
+ * (insert/update/delete), as a single multi-statement string ready for `db.run()`.
35
+ *
36
+ * External content (`content=<table>, content_rowid=<pk>`) means the FTS index stores
37
+ * only the inverted index, never a second copy of the text — the triggers mirror every
38
+ * write into the index. This is the canonical pattern from the SQLite FTS5 docs,
39
+ * generated once so apps don't repeat it per table.
40
+ */
41
+ export function buildFtsSchema(spec) {
42
+ const table = assertIdent(spec.table, 'fts table name');
43
+ const content = assertIdent(spec.contentTable, 'content table name');
44
+ const rowid = assertIdent(spec.contentRowid ?? 'id', 'content rowid column');
45
+ if (spec.columns.length === 0) {
46
+ throw new Error(`buildFtsSchema(${table}): at least one indexed column is required`);
47
+ }
48
+ const cols = spec.columns.map((c) => assertIdent(c, 'indexed column'));
49
+ const tokenize = spec.tokenize ?? 'unicode61 remove_diacritics 2';
50
+ const colList = cols.map(q).join(', ');
51
+ const newVals = cols.map((c) => `new.${q(c)}`).join(', ');
52
+ const oldVals = cols.map((c) => `old.${q(c)}`).join(', ');
53
+ // `INSERT INTO <fts>(<fts>, rowid, …) VALUES('delete', …)` is FTS5's documented way
54
+ // to remove the prior term rows for a content row before re-indexing it.
55
+ return `
56
+ CREATE VIRTUAL TABLE IF NOT EXISTS ${q(table)} USING fts5(
57
+ ${colList},
58
+ content=${q(content)},
59
+ content_rowid=${q(rowid)},
60
+ tokenize=${lit(tokenize)}
61
+ );
62
+ CREATE TRIGGER IF NOT EXISTS ${q(`${table}_ai`)} AFTER INSERT ON ${q(content)} BEGIN
63
+ INSERT INTO ${q(table)}(rowid, ${colList}) VALUES (new.${q(rowid)}, ${newVals});
64
+ END;
65
+ CREATE TRIGGER IF NOT EXISTS ${q(`${table}_ad`)} AFTER DELETE ON ${q(content)} BEGIN
66
+ INSERT INTO ${q(table)}(${q(table)}, rowid, ${colList}) VALUES ('delete', old.${q(rowid)}, ${oldVals});
67
+ END;
68
+ CREATE TRIGGER IF NOT EXISTS ${q(`${table}_au`)} AFTER UPDATE ON ${q(content)} BEGIN
69
+ INSERT INTO ${q(table)}(${q(table)}, rowid, ${colList}) VALUES ('delete', old.${q(rowid)}, ${oldVals});
70
+ INSERT INTO ${q(table)}(rowid, ${colList}) VALUES (new.${q(rowid)}, ${newVals});
71
+ END;`.trim();
72
+ }
73
+ /** Create every table in `specs` and its triggers on `db` (one `db.run` per spec). */
74
+ export function applyFtsSchema(db, ...specs) {
75
+ for (const spec of specs)
76
+ db.run(buildFtsSchema(spec));
77
+ }
78
+ function snippetExpr(table, o) {
79
+ const col = int(o.column ?? -1, -1);
80
+ const start = lit(o.startMatch ?? '<b>');
81
+ const end = lit(o.endMatch ?? '</b>');
82
+ const ell = lit(o.ellipsis ?? '…');
83
+ const tokens = Math.min(64, Math.max(1, int(o.maxTokens ?? 16, 16)));
84
+ return `snippet(${q(table)}, ${col}, ${start}, ${end}, ${ell}, ${tokens}) AS snippet`;
85
+ }
86
+ function highlightExpr(table, o) {
87
+ const col = Math.max(0, int(o.column, 0));
88
+ const start = lit(o.startMatch ?? '<b>');
89
+ const end = lit(o.endMatch ?? '</b>');
90
+ return `highlight(${q(table)}, ${col}, ${start}, ${end}) AS highlight`;
91
+ }
92
+ /**
93
+ * Build a ranked-search facade over the FTS5 tables named in `opts.tables` (the set
94
+ * `rebuildAll()` iterates). The `db` handle is the same one the app uses — one
95
+ * connection, one WAL.
96
+ */
97
+ export function createFts5Search(db, opts = {}) {
98
+ const tables = (opts.tables ?? []).map((t) => assertIdent(t, 'fts table name'));
99
+ const facade = {
100
+ search(o) {
101
+ const match = sanitizeFtsQuery(o.query);
102
+ if (!match)
103
+ return [];
104
+ const table = assertIdent(o.table, 'fts table name');
105
+ const projection = ['rowid', 'rank'];
106
+ if (o.snippet)
107
+ projection.push(snippetExpr(table, o.snippet));
108
+ if (o.highlight)
109
+ projection.push(highlightExpr(table, o.highlight));
110
+ const sql = `SELECT ${projection.join(', ')} FROM ${q(table)} WHERE ${q(table)} MATCH ? ORDER BY rank LIMIT ? OFFSET ?`;
111
+ return db
112
+ .query(sql)
113
+ .all(match, Math.max(0, int(o.limit ?? 20, 20)), Math.max(0, int(o.offset ?? 0, 0)));
114
+ },
115
+ rebuild(table) {
116
+ const t = assertIdent(table, 'fts table name');
117
+ db.run(`INSERT INTO ${q(t)}(${q(t)}) VALUES('rebuild')`);
118
+ },
119
+ rebuildAll() {
120
+ for (const t of tables)
121
+ facade.rebuild(t);
122
+ },
123
+ clear(table) {
124
+ const t = assertIdent(table, 'fts table name');
125
+ db.run(`INSERT INTO ${q(t)}(${q(t)}) VALUES('delete-all')`);
126
+ },
127
+ optimize(table) {
128
+ const t = assertIdent(table, 'fts table name');
129
+ db.run(`INSERT INTO ${q(t)}(${q(t)}) VALUES('optimize')`);
130
+ },
131
+ };
132
+ return facade;
133
+ }
134
+ // ── Query sanitization ─────────────────────────────────────────────────────────
135
+ /**
136
+ * Convert arbitrary user input into a safe FTS5 MATCH expression: every run of
137
+ * letters/digits/underscore becomes a quoted prefix token (`"term"*`), joined by
138
+ * spaces (FTS5's implicit AND). All FTS5 operators (`" * : ^ ( ) - + NEAR …`) are
139
+ * dropped with the surrounding punctuation, so the query can neither inject syntax
140
+ * nor raise a parse error. Returns `''` when nothing searchable remains.
141
+ *
142
+ * Examples: `hello` → `"hello"*`; `hello world` → `"hello"* "world"*` (both must
143
+ * match); `DROP TABLE` → `"DROP"* "TABLE"*` (a literal search, harmless).
144
+ */
145
+ export function sanitizeFtsQuery(raw, opts = {}) {
146
+ const tokens = raw.match(/[\p{L}\p{N}_]+/gu);
147
+ if (!tokens || tokens.length === 0)
148
+ return '';
149
+ // 'all' (default) = AND-of-prefix — precision, right for search-as-you-type.
150
+ // 'any' = OR-of-prefix — recall, right for retrieval from a natural-language
151
+ // question where requiring every word ("what do I have about…") matches nothing;
152
+ // bm25 still ranks docs hitting more/rarer terms first.
153
+ return tokens.map((t) => `"${t}"*`).join(opts.mode === 'any' ? ' OR ' : ' ');
154
+ }
@@ -0,0 +1,44 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * DB-layer health probes for `bun:sqlite`. Each is a small, self-describing descriptor
4
+ * — `name` + `description` + an async `check()` returning a status — so it can be
5
+ * rendered in an operator console and drive readiness from one source.
6
+ *
7
+ * The shape is intentionally dependency-free (it does NOT import cwip's health types):
8
+ * apps adapt a descriptor into cwip's `HealthCheck` registry with a trivial map, e.g.
9
+ *
10
+ * ```ts
11
+ * registry.register(...createSqliteHealthChecks(db, {}).map((p) => async () => {
12
+ * const r = await p.check();
13
+ * return { id: p.name, title: p.description, category: 'Database',
14
+ * severity: 'warn', status: r.status, detail: r.message ?? '', remediation: [] };
15
+ * }));
16
+ * ```
17
+ *
18
+ * `status` values (`ok`/`warn`/`error`) already line up with cwip's `HealthStatus`.
19
+ */
20
+ export type SqliteHealthStatus = 'ok' | 'warn' | 'error';
21
+ export interface SqliteHealthResult {
22
+ status: SqliteHealthStatus;
23
+ /** Human-readable detail for the current status. */
24
+ message?: string;
25
+ }
26
+ export interface SqliteHealthCheck {
27
+ /** Stable id, e.g. `'sqlite:integrity'`. */
28
+ name: string;
29
+ /** Short human description. */
30
+ description: string;
31
+ check(): Promise<SqliteHealthResult>;
32
+ }
33
+ export interface SqliteHealthOpts {
34
+ /** Warn when the DB file exceeds this many bytes. Default: 500 MB. */
35
+ maxSizeBytes?: number;
36
+ /** Warn when the WAL holds more than this many pages. Default: 1000. */
37
+ maxWalPages?: number;
38
+ }
39
+ /**
40
+ * Build the SQLite health probes for `db`. Use `quick_check` (fast, still catches
41
+ * corruption) in the loop; expose the exhaustive `integrity_check` as an
42
+ * admin-triggered action elsewhere.
43
+ */
44
+ export declare function createSqliteHealthChecks(db: Database, opts?: SqliteHealthOpts): SqliteHealthCheck[];
@@ -0,0 +1,52 @@
1
+ const BYTES_PER_MB = 1_048_576;
2
+ const mb = (bytes) => `${(bytes / BYTES_PER_MB).toFixed(1)}MB`;
3
+ /**
4
+ * Build the SQLite health probes for `db`. Use `quick_check` (fast, still catches
5
+ * corruption) in the loop; expose the exhaustive `integrity_check` as an
6
+ * admin-triggered action elsewhere.
7
+ */
8
+ export function createSqliteHealthChecks(db, opts = {}) {
9
+ const maxSizeBytes = opts.maxSizeBytes ?? 500 * BYTES_PER_MB;
10
+ const maxWalPages = opts.maxWalPages ?? 1000;
11
+ return [
12
+ {
13
+ name: 'sqlite:integrity',
14
+ description: 'SQLite quick_check integrity',
15
+ async check() {
16
+ const row = db.query('PRAGMA quick_check').get();
17
+ const outcome = row ? Object.values(row)[0] : undefined;
18
+ return outcome === 'ok'
19
+ ? { status: 'ok', message: 'ok' }
20
+ : { status: 'error', message: outcome ?? 'unknown' };
21
+ },
22
+ },
23
+ {
24
+ name: 'sqlite:size',
25
+ description: 'DB file size within threshold',
26
+ async check() {
27
+ const info = db
28
+ .query('SELECT page_count, page_size FROM pragma_page_count(), pragma_page_size()')
29
+ .get();
30
+ const bytes = (info?.page_count ?? 0) * (info?.page_size ?? 4096);
31
+ return bytes > maxSizeBytes
32
+ ? { status: 'warn', message: `DB is ${mb(bytes)} (threshold: ${mb(maxSizeBytes)})` }
33
+ : { status: 'ok', message: mb(bytes) };
34
+ },
35
+ },
36
+ {
37
+ name: 'sqlite:wal',
38
+ description: 'WAL checkpoint lag',
39
+ async check() {
40
+ // PRAGMA wal_checkpoint returns one row {busy, log, checkpointed}; `log` is the
41
+ // total WAL pages, or -1 when the DB is not in WAL mode (treat as 0).
42
+ const row = db
43
+ .query('PRAGMA wal_checkpoint(PASSIVE)')
44
+ .get();
45
+ const pages = row && row.log > 0 ? row.log : 0;
46
+ return pages > maxWalPages
47
+ ? { status: 'warn', message: `WAL has ${pages} pages (threshold: ${maxWalPages})` }
48
+ : { status: 'ok', message: `${pages} WAL pages` };
49
+ },
50
+ },
51
+ ];
52
+ }
@@ -0,0 +1,36 @@
1
+ import { Database } from 'bun:sqlite';
2
+ /**
3
+ * Write-side SQLite primitives — the counterpart to the read-only `browse.ts` /
4
+ * `export.ts`. Both surfaces are deliberately scoped: row import into an EXISTING
5
+ * table and whole-DB snapshot restore. No arbitrary DDL, no schema mutation — the
6
+ * table must already exist, and only its real columns are written.
7
+ *
8
+ * Every operation runs inside a transaction so a mid-operation failure rolls the DB
9
+ * back atomically (stronger than a copy-aside snapshot). Foreign-key enforcement is
10
+ * suspended for the bulk replace/restore and always restored afterward.
11
+ */
12
+ export type ImportMode = 'append' | 'replace';
13
+ export interface ImportResult {
14
+ inserted: number;
15
+ }
16
+ /**
17
+ * Insert already-parsed rows into an existing table. Unknown keys are dropped (only
18
+ * columns that actually exist on the table are written); `replace` clears the table
19
+ * first. Returns the number of rows inserted. Throws {@link UnknownTableError} for a
20
+ * table not in `sqlite_master`.
21
+ */
22
+ export declare function importRowsIntoSqliteTable(db: Database, table: string, rows: Record<string, unknown>[], opts?: {
23
+ mode?: ImportMode;
24
+ }): ImportResult;
25
+ export interface RestoreResult {
26
+ restoredTables: string[];
27
+ totalRows: number;
28
+ }
29
+ /**
30
+ * Restore a `.sqlite` snapshot into the live DB, table by table. For every table
31
+ * present in BOTH the snapshot and the target, the target's rows are replaced by the
32
+ * snapshot's (via `ATTACH` + `INSERT … SELECT`). Tables that exist only in the
33
+ * snapshot (or only in the target) are left untouched — this is a data restore, not
34
+ * a schema migration. Atomic: any failure rolls the whole restore back.
35
+ */
36
+ export declare function restoreSqliteSnapshot(db: Database, bytes: Uint8Array): RestoreResult;
@@ -0,0 +1,108 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { withForeignKeysOff } from 'cwip/sqlite';
3
+ import { rmSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { isKnownTable, quoteIdent, UnknownTableError } from './browse';
7
+ /** Real column names of a table, in declared order. */
8
+ function tableColumns(db, table) {
9
+ return db
10
+ .query(`PRAGMA table_info(${quoteIdent(table)})`)
11
+ .all()
12
+ .map((r) => r.name);
13
+ }
14
+ /**
15
+ * Insert already-parsed rows into an existing table. Unknown keys are dropped (only
16
+ * columns that actually exist on the table are written); `replace` clears the table
17
+ * first. Returns the number of rows inserted. Throws {@link UnknownTableError} for a
18
+ * table not in `sqlite_master`.
19
+ */
20
+ export function importRowsIntoSqliteTable(db, table, rows, opts = {}) {
21
+ if (!isKnownTable(db, table))
22
+ throw new UnknownTableError(table);
23
+ const mode = opts.mode ?? 'append';
24
+ const valid = new Set(tableColumns(db, table));
25
+ // Columns to write = table columns present in at least one row (preserve order).
26
+ const cols = tableColumns(db, table).filter((c) => rows.some((r) => c in r && valid.has(c)));
27
+ const runImport = db.transaction(() => {
28
+ if (mode === 'replace')
29
+ db.run(`DELETE FROM ${quoteIdent(table)}`);
30
+ if (rows.length === 0 || cols.length === 0)
31
+ return 0;
32
+ const placeholders = cols.map(() => '?').join(', ');
33
+ const colList = cols.map(quoteIdent).join(', ');
34
+ const stmt = db.prepare(`INSERT INTO ${quoteIdent(table)} (${colList}) VALUES (${placeholders})`);
35
+ let inserted = 0;
36
+ for (const row of rows) {
37
+ stmt.run(...cols.map((c) => normalizeValue(row[c])));
38
+ inserted++;
39
+ }
40
+ return inserted;
41
+ });
42
+ const inserted = mode === 'replace' ? withForeignKeysOff(db, runImport) : runImport();
43
+ return { inserted };
44
+ }
45
+ /** bun:sqlite binds string/number/bigint/boolean/null/Uint8Array; coerce the rest. */
46
+ function normalizeValue(v) {
47
+ if (v === undefined || v === null)
48
+ return null;
49
+ if (typeof v === 'string' ||
50
+ typeof v === 'number' ||
51
+ typeof v === 'bigint' ||
52
+ typeof v === 'boolean' ||
53
+ v instanceof Uint8Array) {
54
+ return v;
55
+ }
56
+ // Objects/arrays → JSON text (the pragmatic choice for a foreign-format import).
57
+ return JSON.stringify(v);
58
+ }
59
+ /**
60
+ * Restore a `.sqlite` snapshot into the live DB, table by table. For every table
61
+ * present in BOTH the snapshot and the target, the target's rows are replaced by the
62
+ * snapshot's (via `ATTACH` + `INSERT … SELECT`). Tables that exist only in the
63
+ * snapshot (or only in the target) are left untouched — this is a data restore, not
64
+ * a schema migration. Atomic: any failure rolls the whole restore back.
65
+ */
66
+ export function restoreSqliteSnapshot(db, bytes) {
67
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
68
+ const tmp = join(tmpdir(), `cb-restore-${stamp}-${Math.random().toString(36).slice(2)}.sqlite`);
69
+ writeFileSync(tmp, bytes);
70
+ try {
71
+ // Validate it opens as SQLite and enumerate its user tables.
72
+ const snap = new Database(tmp, { readonly: true });
73
+ let snapTables;
74
+ try {
75
+ snapTables = snap
76
+ .query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
77
+ .all()
78
+ .map((r) => r.name);
79
+ }
80
+ finally {
81
+ snap.close();
82
+ }
83
+ const targets = snapTables.filter((t) => isKnownTable(db, t));
84
+ let totalRows = 0;
85
+ withForeignKeysOff(db, () => {
86
+ db.run('ATTACH DATABASE ? AS restore_src', [tmp]);
87
+ try {
88
+ const run = db.transaction(() => {
89
+ for (const t of targets) {
90
+ const q = quoteIdent(t);
91
+ db.run(`DELETE FROM main.${q}`);
92
+ db.run(`INSERT INTO main.${q} SELECT * FROM restore_src.${q}`);
93
+ totalRows +=
94
+ db.query(`SELECT COUNT(*) AS n FROM main.${q}`).get()?.n ?? 0;
95
+ }
96
+ });
97
+ run();
98
+ }
99
+ finally {
100
+ db.run('DETACH DATABASE restore_src');
101
+ }
102
+ });
103
+ return { restoredTables: targets, totalRows };
104
+ }
105
+ finally {
106
+ rmSync(tmp, { force: true });
107
+ }
108
+ }