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