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