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
package/package.json
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cursedbelt-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "ISC",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "The app-facing Bun/Hono server tier of the cursedbelt split — storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
7
|
+
"cursed": {
|
|
8
|
+
"schemaVersion": 1,
|
|
9
|
+
"app": {
|
|
10
|
+
"name": "cursedbelt-server",
|
|
11
|
+
"commands": {
|
|
12
|
+
"test": "test",
|
|
13
|
+
"typecheck": "typecheck",
|
|
14
|
+
"build": "build"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clean": "rm -rf dist",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
21
|
+
"test": "NODE_ENV=development bun test src",
|
|
22
|
+
"build": "bun run clean && tsc -p tsconfig.build.json",
|
|
23
|
+
"paths": "bun ../../tools/check-paths.ts .",
|
|
24
|
+
"verify": "bun run paths && bun run typecheck && bun run build && bun run test",
|
|
25
|
+
"prepublishOnly": "bun run verify"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"src"
|
|
30
|
+
],
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/server/index.d.ts",
|
|
34
|
+
"bun": "./src/server/index.ts",
|
|
35
|
+
"source": "./src/server/index.ts",
|
|
36
|
+
"import": "./dist/server/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./activity": {
|
|
39
|
+
"types": "./dist/server/activity/index.d.ts",
|
|
40
|
+
"bun": "./src/server/activity/index.ts",
|
|
41
|
+
"source": "./src/server/activity/index.ts",
|
|
42
|
+
"import": "./dist/server/activity/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./context": {
|
|
45
|
+
"types": "./dist/server/context.d.ts",
|
|
46
|
+
"bun": "./src/server/context.ts",
|
|
47
|
+
"source": "./src/server/context.ts",
|
|
48
|
+
"import": "./dist/server/context.js"
|
|
49
|
+
},
|
|
50
|
+
"./dropzone": {
|
|
51
|
+
"types": "./dist/server/dropzone/index.d.ts",
|
|
52
|
+
"bun": "./src/server/dropzone/index.ts",
|
|
53
|
+
"source": "./src/server/dropzone/index.ts",
|
|
54
|
+
"import": "./dist/server/dropzone/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./errors": {
|
|
57
|
+
"types": "./dist/server/errors.d.ts",
|
|
58
|
+
"bun": "./src/server/errors.ts",
|
|
59
|
+
"source": "./src/server/errors.ts",
|
|
60
|
+
"import": "./dist/server/errors.js"
|
|
61
|
+
},
|
|
62
|
+
"./guard": {
|
|
63
|
+
"types": "./dist/server/guard/index.d.ts",
|
|
64
|
+
"bun": "./src/server/guard/index.ts",
|
|
65
|
+
"source": "./src/server/guard/index.ts",
|
|
66
|
+
"import": "./dist/server/guard/index.js"
|
|
67
|
+
},
|
|
68
|
+
"./guard/revocations": {
|
|
69
|
+
"types": "./dist/server/guard/revocationStore.d.ts",
|
|
70
|
+
"bun": "./src/server/guard/revocationStore.ts",
|
|
71
|
+
"source": "./src/server/guard/revocationStore.ts",
|
|
72
|
+
"import": "./dist/server/guard/revocationStore.js"
|
|
73
|
+
},
|
|
74
|
+
"./incidents": {
|
|
75
|
+
"types": "./dist/server/incidents/index.d.ts",
|
|
76
|
+
"bun": "./src/server/incidents/index.ts",
|
|
77
|
+
"source": "./src/server/incidents/index.ts",
|
|
78
|
+
"import": "./dist/server/incidents/index.js"
|
|
79
|
+
},
|
|
80
|
+
"./item-crypto": {
|
|
81
|
+
"types": "./dist/server/auth/vault.d.ts",
|
|
82
|
+
"bun": "./src/server/auth/vault.ts",
|
|
83
|
+
"source": "./src/server/auth/vault.ts",
|
|
84
|
+
"import": "./dist/server/auth/vault.js"
|
|
85
|
+
},
|
|
86
|
+
"./jobs": {
|
|
87
|
+
"types": "./dist/server/jobs/index.d.ts",
|
|
88
|
+
"bun": "./src/server/jobs/index.ts",
|
|
89
|
+
"source": "./src/server/jobs/index.ts",
|
|
90
|
+
"import": "./dist/server/jobs/index.js"
|
|
91
|
+
},
|
|
92
|
+
"./login-throttle": {
|
|
93
|
+
"types": "./dist/server/auth/loginThrottle.d.ts",
|
|
94
|
+
"bun": "./src/server/auth/loginThrottle.ts",
|
|
95
|
+
"source": "./src/server/auth/loginThrottle.ts",
|
|
96
|
+
"import": "./dist/server/auth/loginThrottle.js"
|
|
97
|
+
},
|
|
98
|
+
"./master-lock": {
|
|
99
|
+
"types": "./dist/server/master-lock/index.d.ts",
|
|
100
|
+
"bun": "./src/server/master-lock/index.ts",
|
|
101
|
+
"source": "./src/server/master-lock/index.ts",
|
|
102
|
+
"import": "./dist/server/master-lock/index.js"
|
|
103
|
+
},
|
|
104
|
+
"./middleware": {
|
|
105
|
+
"types": "./dist/server/middleware/index.d.ts",
|
|
106
|
+
"bun": "./src/server/middleware/index.ts",
|
|
107
|
+
"source": "./src/server/middleware/index.ts",
|
|
108
|
+
"import": "./dist/server/middleware/index.js"
|
|
109
|
+
},
|
|
110
|
+
"./migration": {
|
|
111
|
+
"types": "./dist/server/migration/runner.d.ts",
|
|
112
|
+
"bun": "./src/server/migration/runner.ts",
|
|
113
|
+
"source": "./src/server/migration/runner.ts",
|
|
114
|
+
"import": "./dist/server/migration/runner.js"
|
|
115
|
+
},
|
|
116
|
+
"./net": {
|
|
117
|
+
"types": "./dist/server/net/outboundGuard.d.ts",
|
|
118
|
+
"bun": "./src/server/net/outboundGuard.ts",
|
|
119
|
+
"source": "./src/server/net/outboundGuard.ts",
|
|
120
|
+
"import": "./dist/server/net/outboundGuard.js"
|
|
121
|
+
},
|
|
122
|
+
"./notifications": {
|
|
123
|
+
"types": "./dist/server/notifications/index.d.ts",
|
|
124
|
+
"bun": "./src/server/notifications/index.ts",
|
|
125
|
+
"source": "./src/server/notifications/index.ts",
|
|
126
|
+
"import": "./dist/server/notifications/index.js"
|
|
127
|
+
},
|
|
128
|
+
"./resume": {
|
|
129
|
+
"types": "./dist/server/resume/index.d.ts",
|
|
130
|
+
"bun": "./src/server/resume/index.ts",
|
|
131
|
+
"source": "./src/server/resume/index.ts",
|
|
132
|
+
"import": "./dist/server/resume/index.js"
|
|
133
|
+
},
|
|
134
|
+
"./retention": {
|
|
135
|
+
"types": "./dist/server/retention/index.d.ts",
|
|
136
|
+
"bun": "./src/server/retention/index.ts",
|
|
137
|
+
"source": "./src/server/retention/index.ts",
|
|
138
|
+
"import": "./dist/server/retention/index.js"
|
|
139
|
+
},
|
|
140
|
+
"./search": {
|
|
141
|
+
"types": "./dist/server/search/index.d.ts",
|
|
142
|
+
"bun": "./src/server/search/index.ts",
|
|
143
|
+
"source": "./src/server/search/index.ts",
|
|
144
|
+
"import": "./dist/server/search/index.js"
|
|
145
|
+
},
|
|
146
|
+
"./sharing": {
|
|
147
|
+
"types": "./dist/server/sharing/index.d.ts",
|
|
148
|
+
"bun": "./src/server/sharing/index.ts",
|
|
149
|
+
"source": "./src/server/sharing/index.ts",
|
|
150
|
+
"import": "./dist/server/sharing/index.js"
|
|
151
|
+
},
|
|
152
|
+
"./sqlite": {
|
|
153
|
+
"types": "./dist/server/sqlite/index.d.ts",
|
|
154
|
+
"bun": "./src/server/sqlite/index.ts",
|
|
155
|
+
"source": "./src/server/sqlite/index.ts",
|
|
156
|
+
"import": "./dist/server/sqlite/index.js"
|
|
157
|
+
},
|
|
158
|
+
"./storage": {
|
|
159
|
+
"types": "./dist/server/storage/index.d.ts",
|
|
160
|
+
"bun": "./src/server/storage/index.ts",
|
|
161
|
+
"source": "./src/server/storage/index.ts",
|
|
162
|
+
"import": "./dist/server/storage/index.js"
|
|
163
|
+
},
|
|
164
|
+
"./storage/types": {
|
|
165
|
+
"types": "./dist/server/storage/types.d.ts",
|
|
166
|
+
"bun": "./src/server/storage/types.ts",
|
|
167
|
+
"source": "./src/server/storage/types.ts",
|
|
168
|
+
"import": "./dist/server/storage/types.js"
|
|
169
|
+
},
|
|
170
|
+
"./sync": {
|
|
171
|
+
"types": "./dist/server/sync/index.d.ts",
|
|
172
|
+
"bun": "./src/server/sync/index.ts",
|
|
173
|
+
"source": "./src/server/sync/index.ts",
|
|
174
|
+
"import": "./dist/server/sync/index.js"
|
|
175
|
+
},
|
|
176
|
+
"./ws": {
|
|
177
|
+
"types": "./dist/server/ws/index.d.ts",
|
|
178
|
+
"bun": "./src/server/ws/index.ts",
|
|
179
|
+
"source": "./src/server/ws/index.ts",
|
|
180
|
+
"import": "./dist/server/ws/index.js"
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"dependencies": {
|
|
184
|
+
"cursedbelt-core": "^1.0.0",
|
|
185
|
+
"cwip": "^3.0.2",
|
|
186
|
+
"jose": "^6.2.3"
|
|
187
|
+
},
|
|
188
|
+
"peerDependencies": {
|
|
189
|
+
"hono": ">=4",
|
|
190
|
+
"zod": "^4.4.0",
|
|
191
|
+
"@node-rs/argon2": "^2.0.2",
|
|
192
|
+
"kysely": "^0.28.17",
|
|
193
|
+
"kysely-bun-sqlite": "^0.4.0",
|
|
194
|
+
"otplib": "^13.4.1",
|
|
195
|
+
"plainjob": "^0.0.14",
|
|
196
|
+
"sharp": "^0.35.2"
|
|
197
|
+
},
|
|
198
|
+
"peerDependenciesMeta": {
|
|
199
|
+
"@node-rs/argon2": {
|
|
200
|
+
"optional": true
|
|
201
|
+
},
|
|
202
|
+
"kysely": {
|
|
203
|
+
"optional": true
|
|
204
|
+
},
|
|
205
|
+
"kysely-bun-sqlite": {
|
|
206
|
+
"optional": true
|
|
207
|
+
},
|
|
208
|
+
"otplib": {
|
|
209
|
+
"optional": true
|
|
210
|
+
},
|
|
211
|
+
"plainjob": {
|
|
212
|
+
"optional": true
|
|
213
|
+
},
|
|
214
|
+
"sharp": {
|
|
215
|
+
"optional": true
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"devDependencies": {
|
|
219
|
+
"@biomejs/biome": "^2.5.0",
|
|
220
|
+
"@happy-dom/global-registrator": "^20.10.6",
|
|
221
|
+
"@node-rs/argon2": "^2.0.2",
|
|
222
|
+
"@types/bun": "^1.3.14",
|
|
223
|
+
"@types/node": "^24",
|
|
224
|
+
"hono": "4.12.28",
|
|
225
|
+
"kysely": "^0.28.17",
|
|
226
|
+
"kysely-bun-sqlite": "^0.4.0",
|
|
227
|
+
"otplib": "^13.4.1",
|
|
228
|
+
"papaparse": "^5",
|
|
229
|
+
"plainjob": "^0.0.14",
|
|
230
|
+
"sharp": "^0.35.2",
|
|
231
|
+
"typescript": "^6.0.3",
|
|
232
|
+
"zod": "4.4.3"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import pkg from '../package.json';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Every declared dependency must be reachable from an import (2026-09-13).
|
|
6
|
+
*
|
|
7
|
+
* `dependencies` is not advice — a consumer installs all of it to render one
|
|
8
|
+
* button. `peerDependencies` is a promise that installing the peer lights a
|
|
9
|
+
* feature up. Both rot silently: a component gets deleted, its package stays
|
|
10
|
+
* declared, and nothing anywhere reds. At the time this was written the manifest
|
|
11
|
+
* carried three runtime deps and five optional peers that no file imported.
|
|
12
|
+
*
|
|
13
|
+
* 🔴 The reason this matches an IMPORT SPECIFIER and scans EVERY file type,
|
|
14
|
+
* rather than grepping .ts for the bare name:
|
|
15
|
+
*
|
|
16
|
+
* - A .ts-only grep says `tw-animate-css` is dead. It is not — it is pulled in
|
|
17
|
+
* from CSS (`@import` in src/styles.css), and deleting it would have shipped
|
|
18
|
+
* a package whose stylesheet cannot resolve. That is the exact mistake this
|
|
19
|
+
* session made on its first pass.
|
|
20
|
+
* - A bare-name grep says everything is alive, because names like `diff` and
|
|
21
|
+
* `marked` appear in prose in hundreds of files. A gate that cannot fail is
|
|
22
|
+
* not a gate.
|
|
23
|
+
*
|
|
24
|
+
* devDependencies are deliberately NOT checked: several are installed only so
|
|
25
|
+
* this repo can typecheck and test an OPTIONAL peer's integration, so "nothing
|
|
26
|
+
* imports it by name" is the expected state there, not a defect.
|
|
27
|
+
*/
|
|
28
|
+
const ROOTS = ['src', 'scripts'] as const;
|
|
29
|
+
|
|
30
|
+
// Reading a font or a screenshot as text costs time and can never match.
|
|
31
|
+
const BINARY = /\.(png|jpe?g|gif|webp|avif|ico|mp4|webm|mp3|wav|pdf|zip|gz|woff2?|ttf|otf|eot|wasm)$/i;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `from 'x'` · `import 'x/y.css'` · `import('x')` · `require('x')` · `@import "x"`,
|
|
35
|
+
* with or without a subpath. Anchored on the specifier quote so a name mentioned
|
|
36
|
+
* in prose or in a comment does not count as a use.
|
|
37
|
+
*/
|
|
38
|
+
const RE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
39
|
+
|
|
40
|
+
const importsPackage = (name: string): RegExp =>
|
|
41
|
+
new RegExp(`(?:from|import|require|@import)\\s*\\(?\\s*['"]${name.replace(RE_SPECIAL, '\\$&')}(?:/[^'"]*)?['"]`);
|
|
42
|
+
|
|
43
|
+
// 🔴 Bun.Glob + Bun.file, NOT node:fs — this package's test preload virtualizes
|
|
44
|
+
// node:fs, so a readdirSync/readFileSync scan reads MOCK data and passes against
|
|
45
|
+
// files it never opened. Same trap as publishShape.spec.ts; see its header.
|
|
46
|
+
let cache: Promise<string[]> | undefined;
|
|
47
|
+
const sources = (): Promise<string[]> => {
|
|
48
|
+
cache ??= (async () => {
|
|
49
|
+
const repo = `${import.meta.dir}/..`;
|
|
50
|
+
const texts: string[] = [];
|
|
51
|
+
for (const root of ROOTS) {
|
|
52
|
+
const dir = `${repo}/${root}`;
|
|
53
|
+
for (const rel of new Bun.Glob('**/*').scanSync({ cwd: dir, onlyFiles: true })) {
|
|
54
|
+
if (BINARY.test(rel)) continue;
|
|
55
|
+
texts.push(await Bun.file(`${dir}/${rel}`).text());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return texts;
|
|
59
|
+
})();
|
|
60
|
+
return cache;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const unimported = async (block: Record<string, string> | undefined): Promise<string[]> => {
|
|
64
|
+
const texts = await sources();
|
|
65
|
+
return Object.keys(block ?? {}).filter((name) => {
|
|
66
|
+
const re = importsPackage(name);
|
|
67
|
+
return !texts.some((t) => re.test(t));
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe('declared dependencies are imported', () => {
|
|
72
|
+
it('has scanned a real source tree', async () => {
|
|
73
|
+
// Guards the whole file: if the glob or the fs mock ever returns nothing,
|
|
74
|
+
// every assertion below passes vacuously and the gate quietly dies.
|
|
75
|
+
expect((await sources()).length).toBeGreaterThan(100);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
for (const field of ['dependencies', 'peerDependencies'] as const) {
|
|
79
|
+
it(`${field} names nothing the code never imports`, async () => {
|
|
80
|
+
const dead = await unimported((pkg as Record<string, unknown>)[field] as Record<string, string>);
|
|
81
|
+
expect(
|
|
82
|
+
dead,
|
|
83
|
+
`declared in ${field} but imported nowhere in ${ROOTS.join('/')} — import it or drop it:\n ${dead.join('\n ')}`,
|
|
84
|
+
).toEqual([]);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { readdirSync, rmSync, statSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import pkg from '../package.json';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A LEAF subpath's whole promise is what it does NOT pull in. Nothing in this repo
|
|
8
|
+
* measured that until now (2026-09-15), so the promise was kept by hand — and it was
|
|
9
|
+
* broken twice in two days, in the same shape, one level apart.
|
|
10
|
+
*
|
|
11
|
+
* ## The measurement this exists because of
|
|
12
|
+
*
|
|
13
|
+
* `apps/collections/src/kit/binaryStore.ts` needed exactly one thing from here:
|
|
14
|
+
*
|
|
15
|
+
* import type { FileTokenClaims } from "cursedbelt/server/storage";
|
|
16
|
+
*
|
|
17
|
+
* A TYPE — TypeScript erases it, so the app should have paid nothing at runtime. What it
|
|
18
|
+
* actually paid was:
|
|
19
|
+
*
|
|
20
|
+
* error: Cannot find package 'otplib' from
|
|
21
|
+
* …/node_modules/cursedbelt/src/server/auth/totp.ts
|
|
22
|
+
*
|
|
23
|
+
* …and four unrelated route suites died before anything was measured. `server/storage`'s
|
|
24
|
+
* index is a barrel; the barrel reached `streamToken.ts`, which imported the `../auth`
|
|
25
|
+
* INDEX for two jose functions, and that index reaches `totp.ts` → `otplib`, an optional
|
|
26
|
+
* peer. (`sessionStore.ts` takes its Kysely db as a TYPE, so the `kysely` half of the same
|
|
27
|
+
* trap arrives through the `./server` barrel's `db/kysely.ts` instead — measured, not
|
|
28
|
+
* assumed: bundling `./server/storage` externals before this change listed `otplib` and
|
|
29
|
+
* `hono/cookie`, never `kysely`.) The app declared `otplib` as a direct dependency to get
|
|
30
|
+
* its route suites back — a one-time-password library installed to satisfy a claim shape.
|
|
31
|
+
*
|
|
32
|
+
* That is the exact shape 2.5.1 shipped `./server/login-throttle` to fix, one level
|
|
33
|
+
* deeper, and the rule written down after it ("never import the `cursedbelt/server`
|
|
34
|
+
* barrel for one primitive") could not catch it, because `./server/storage` IS a leaf by
|
|
35
|
+
* that rule's standard and has a barrel of its own. A rule that only a human can apply
|
|
36
|
+
* gets re-broken by the next module that looks like a leaf. So: a check.
|
|
37
|
+
*
|
|
38
|
+
* ## What it does
|
|
39
|
+
*
|
|
40
|
+
* Assembles a throwaway consumer under `$TMPDIR` — a `node_modules/cursedbelt` holding
|
|
41
|
+
* this package's REAL package.json (so the real `exports` map is what resolves) and
|
|
42
|
+
* nothing else but the leaf source files themselves — then spawns bun in it and imports
|
|
43
|
+
* each leaf by its PUBLIC specifier. No peers are installed, no other file of this package
|
|
44
|
+
* is even on disk, so any import that survives transpilation fails the import and this
|
|
45
|
+
* spec with it. `import type` is erased and costs nothing, which is the whole point:
|
|
46
|
+
* `verbatimModuleSyntax` is on, so a type import that is not written `import type` is
|
|
47
|
+
* already a typecheck error, and source and `dist` therefore carry the same module graph.
|
|
48
|
+
*
|
|
49
|
+
* ## 🔴 The trap that makes the naive version of this check a lie
|
|
50
|
+
*
|
|
51
|
+
* Bun AUTO-INSTALLS a missing bare import out of `~/.bun/install/cache` when the walk up
|
|
52
|
+
* from the importing file finds NO `node_modules` at all. Measured while building this, in
|
|
53
|
+
* a scratch directory holding two files and nothing else: `import { authenticator } from
|
|
54
|
+
* 'otplib'` resolved — from
|
|
55
|
+
* `~/.bun/install/cache/otplib@13.5.0@@@1/dist/index.js` — and the only error was a missing
|
|
56
|
+
* named export. An "empty fixture" therefore proves NOTHING about what a leaf drags: every
|
|
57
|
+
* peer in the cache answers.
|
|
58
|
+
*
|
|
59
|
+
* Two things hold the line, and the second is the one that matters:
|
|
60
|
+
*
|
|
61
|
+
* 1. `bunfig.toml` with `[install] auto = "disable"` in the fixture root. Note what this
|
|
62
|
+
* is and is not: with a `node_modules/` present (as here) bun does not auto-install
|
|
63
|
+
* anyway — removing the line and re-running left the negative control failing exactly
|
|
64
|
+
* as before. It is the second lock, for the day this fixture is refactored into one
|
|
65
|
+
* that has no `node_modules` and the trap silently comes back.
|
|
66
|
+
* 2. The NEGATIVE CONTROL: a file importing `otplib` and nothing else is copied in beside
|
|
67
|
+
* the leaves, and this spec fails if importing it SUCCEEDS. Whatever makes peers
|
|
68
|
+
* resolvable — auto-install, a `node_modules` above the scratch root, a future bun
|
|
69
|
+
* default — the control sees it and reds, instead of letting every assertion below
|
|
70
|
+
* pass vacuously.
|
|
71
|
+
*
|
|
72
|
+
* ## 🔴 Verified failing, 2026-09-15, before it was trusted — every branch
|
|
73
|
+
*
|
|
74
|
+
* · `import 'otplib'` added to `src/server/storage/types.ts`
|
|
75
|
+
* → "cursedbelt/server/storage/types must import nothing at runtime … Cannot find
|
|
76
|
+
* package 'otplib' imported from …/node_modules/cursedbelt/src/server/storage/types.ts"
|
|
77
|
+
* · the same, but `import { createStreamTokenService } from './streamToken'` — a
|
|
78
|
+
* relative import of a module inside this package
|
|
79
|
+
* → red too: "Cannot find module './streamToken'". A leaf may not reach sideways
|
|
80
|
+
* either, which is why ONLY the leaf files are copied into the fixture.
|
|
81
|
+
* · `./server/storage/types` re-pointed at `src/server/storage/index.ts`, the barrel it
|
|
82
|
+
* was split out of
|
|
83
|
+
* → red: "Cannot find module './videoDerivativeKeys'". A barrel cannot pass this.
|
|
84
|
+
* · the negative control re-pointed at `loginThrottle.ts`, which imports nothing, so the
|
|
85
|
+
* fixture stopped demonstrating that peers are unreachable
|
|
86
|
+
* → red on the control itself: "the fixture resolved 'otplib' … Every assertion below
|
|
87
|
+
* is vacuous until that is fixed". The vacuity guard fires.
|
|
88
|
+
* · `src/server/private-media/unlock.ts` put back to `import … from '../auth'` — the
|
|
89
|
+
* second, two-hop edge, which grep for `../auth` inside `src/server/storage` cannot see
|
|
90
|
+
* → red: "importing cursedbelt/server/storage statically pulls these OPTIONAL peers
|
|
91
|
+
* … + otplib". This is the branch that found that edge in the first place.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
const REPO = fileURLToPath(new URL('..', import.meta.url));
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Every subpath that promises to cost a consumer NOTHING at runtime. A leaf earns its
|
|
98
|
+
* place here by being the answer to "I want one type / one primitive out of a module
|
|
99
|
+
* whose index is heavy" — the entry names the runtime symbol proving the module really
|
|
100
|
+
* evaluated, so an accidentally-empty module can never pass this vacuously.
|
|
101
|
+
*
|
|
102
|
+
* The file is read from `exports`, never hard-coded: re-target the subpath and the check
|
|
103
|
+
* follows it to the new file rather than going quietly green against the old one.
|
|
104
|
+
*/
|
|
105
|
+
const LEAVES = [
|
|
106
|
+
{
|
|
107
|
+
subpath: './storage/types',
|
|
108
|
+
/** Claim/config shapes + the keyspace helpers. apps/collections wants `FileTokenClaims`. */
|
|
109
|
+
evaluates: 'fsSourceKey',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
subpath: './login-throttle',
|
|
113
|
+
/** The failed-login backoff. Every app with a login door; shipped as 2.5.1. */
|
|
114
|
+
evaluates: 'createLoginThrottle',
|
|
115
|
+
},
|
|
116
|
+
] as const;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Imports `otplib` and nothing else, so its failure message is unambiguous. It is the
|
|
120
|
+
* proof that the fixture has no peers — not an assertion about `totp.ts` itself.
|
|
121
|
+
*/
|
|
122
|
+
const CONTROL = 'src/server/auth/totp.ts';
|
|
123
|
+
|
|
124
|
+
type Outcome = { ok: true; keys: string[] } | { ok: false; error: string };
|
|
125
|
+
|
|
126
|
+
const exportsMap = pkg.exports as Record<string, { bun?: string } | string>;
|
|
127
|
+
|
|
128
|
+
/** `./src/server/storage/types.ts` → `src/server/storage/types.ts`. */
|
|
129
|
+
const sourceOf = (subpath: string): string => {
|
|
130
|
+
const entry = exportsMap[subpath];
|
|
131
|
+
const bun = typeof entry === 'object' && entry !== null ? entry.bun : undefined;
|
|
132
|
+
if (typeof bun !== 'string') {
|
|
133
|
+
throw new Error(`package.json exports has no \`bun\` target for "${subpath}"`);
|
|
134
|
+
}
|
|
135
|
+
return bun.replace(/^\.\//, '');
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* 🔴 The fixture must sit OUTSIDE this repo, and where it lands is not a detail.
|
|
140
|
+
*
|
|
141
|
+
* `scripts/testTmpRoot.ts` redirects `$TMPDIR` per process, and when `$FORGE_STATE`
|
|
142
|
+
* is unset — a bare checkout with no generation around it — its fallback is
|
|
143
|
+
* deliberately repo-local: `<repo>/.agent.noindex/tmp/<pid>`. For every other suite
|
|
144
|
+
* that is exactly right. For this one it is fatal: bun resolves a bare import by
|
|
145
|
+
* walking node_modules UP to the filesystem root, so a fixture under the repo finds
|
|
146
|
+
* `<repo>/node_modules/otplib` and every peer this check exists to prove unreachable
|
|
147
|
+
* becomes reachable.
|
|
148
|
+
*
|
|
149
|
+
* Measured 2026-09-15: with `$FORGE_STATE` set the negative control failed correctly;
|
|
150
|
+
* with it unset the SAME control resolved `otplib` out of this repo's own
|
|
151
|
+
* `node_modules` and the guard fired — *"the fixture resolved 'otplib' … Every
|
|
152
|
+
* assertion below is vacuous until that is fixed"*. So `bun run verify` was green
|
|
153
|
+
* under the runner and red in a plain shell, for a reason that has nothing to do with
|
|
154
|
+
* the code under test. The control did its job; the fixture's address was the bug.
|
|
155
|
+
*
|
|
156
|
+
* Resolution here is therefore explicit rather than inherited. The redirected root is
|
|
157
|
+
* used when it lies outside the repo (the normal case — the runner always sets
|
|
158
|
+
* `$FORGE_STATE`, and the preload's sweep then cleans up after a killed run). When it
|
|
159
|
+
* does not, this hops to `$HOME`, which `resolveTestTmpRoot` refuses for the general
|
|
160
|
+
* default and which this check genuinely requires: an isolated root is the whole
|
|
161
|
+
* measurement. Stale siblings are swept by mtime, the same way the preload does it,
|
|
162
|
+
* so the hop cannot accumulate.
|
|
163
|
+
*/
|
|
164
|
+
const REPO_PREFIX = `${REPO.replace(/\/$/, '')}/`;
|
|
165
|
+
|
|
166
|
+
const fixtureRoot = (): string => {
|
|
167
|
+
const redirected = process.env.TMPDIR;
|
|
168
|
+
if (redirected !== undefined && !`${redirected}/`.startsWith(REPO_PREFIX)) {
|
|
169
|
+
return `${redirected}/leaf-subpath-fixture`;
|
|
170
|
+
}
|
|
171
|
+
const home = process.env.HOME?.trim();
|
|
172
|
+
if (home === undefined || home === '') {
|
|
173
|
+
throw new Error(
|
|
174
|
+
'no isolated root available for the leaf fixture ($TMPDIR is inside this repo and $HOME is unset) — ' +
|
|
175
|
+
'a check that cannot isolate itself is not a passing check',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const outside = `${home}/.${pkg.name}-leaf-fixture.noindex`;
|
|
179
|
+
try {
|
|
180
|
+
const cutoff = Date.now() - 6 * 60 * 60 * 1000;
|
|
181
|
+
for (const entry of readdirSync(outside)) {
|
|
182
|
+
const path = `${outside}/${entry}`;
|
|
183
|
+
try {
|
|
184
|
+
if (statSync(path).mtimeMs < cutoff) rmSync(path, { recursive: true, force: true });
|
|
185
|
+
} catch {
|
|
186
|
+
// racing another run's sweep is fine — whoever wins, it goes
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
// no root yet: Bun.write creates it below
|
|
191
|
+
}
|
|
192
|
+
return `${outside}/${process.pid}/leaf-subpath-fixture`;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const run = async (): Promise<Record<string, Outcome>> => {
|
|
196
|
+
const root = fixtureRoot();
|
|
197
|
+
const pkgDir = `${root}/node_modules/${pkg.name}`;
|
|
198
|
+
|
|
199
|
+
// The second lock against bun's auto-install (see the header). The negative control is the
|
|
200
|
+
// first one, and the one that would catch this line being wrong.
|
|
201
|
+
await Bun.write(`${root}/bunfig.toml`, '[install]\nauto = "disable"\n');
|
|
202
|
+
// The REAL manifest: the export map under test, resolved exactly as a consumer resolves it.
|
|
203
|
+
await Bun.write(`${pkgDir}/package.json`, Bun.file(`${REPO}/package.json`));
|
|
204
|
+
|
|
205
|
+
const targets: Array<{ name: string; specifier: string }> = [];
|
|
206
|
+
for (const leaf of LEAVES) {
|
|
207
|
+
const source = sourceOf(leaf.subpath);
|
|
208
|
+
// 🔴 Bun.file/Bun.write, NOT node:fs — this repo's test preload virtualizes node:fs, so a
|
|
209
|
+
// copy made through it can land in a mock the spawned child cannot see. Same trap as
|
|
210
|
+
// publishShape.spec.ts.
|
|
211
|
+
await Bun.write(`${pkgDir}/${source}`, Bun.file(`${REPO}/${source}`));
|
|
212
|
+
targets.push({ name: leaf.subpath, specifier: `${pkg.name}/${leaf.subpath.slice(2)}` });
|
|
213
|
+
}
|
|
214
|
+
await Bun.write(`${pkgDir}/${CONTROL}`, Bun.file(`${REPO}/${CONTROL}`));
|
|
215
|
+
// By PATH, not by subpath: the control is deliberately not exported, and an
|
|
216
|
+
// ERR_PACKAGE_PATH_NOT_EXPORTED would prove nothing about peer resolution.
|
|
217
|
+
targets.push({ name: 'control', specifier: `./node_modules/${pkg.name}/${CONTROL}` });
|
|
218
|
+
|
|
219
|
+
await Bun.write(
|
|
220
|
+
`${root}/entry.ts`,
|
|
221
|
+
`const targets = ${JSON.stringify(targets)};
|
|
222
|
+
const out = {};
|
|
223
|
+
for (const t of targets) {
|
|
224
|
+
try {
|
|
225
|
+
const mod = await import(t.specifier);
|
|
226
|
+
out[t.name] = { ok: true, keys: Object.keys(mod) };
|
|
227
|
+
} catch (error) {
|
|
228
|
+
out[t.name] = { ok: false, error: String((error && error.message) || error) };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
console.log(JSON.stringify(out));
|
|
232
|
+
`,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const child = Bun.spawnSync(['bun', 'run', 'entry.ts'], {
|
|
236
|
+
cwd: root,
|
|
237
|
+
stdout: 'pipe',
|
|
238
|
+
stderr: 'pipe',
|
|
239
|
+
});
|
|
240
|
+
const stdout = child.stdout.toString().trim();
|
|
241
|
+
if (child.exitCode !== 0 || stdout === '') {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`the fixture could not run at all (exit ${child.exitCode}) — that is a broken check, not a passing one:\n${child.stderr.toString()}`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return JSON.parse(stdout) as Record<string, Outcome>;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* The other half, and the one that caught the SECOND edge. A leaf can be clean while the
|
|
251
|
+
* barrel it was split out of still drags an optional peer, and a barrel is what most apps
|
|
252
|
+
* import — `music`, `family` and `roms` all take `cursedbelt/server/storage` whole.
|
|
253
|
+
*
|
|
254
|
+
* Measured by bundling the subpath's relative graph with every bare import left external
|
|
255
|
+
* and reading what remains. The subpaths listed must reach NO optional peer with a STATIC
|
|
256
|
+
* import: a static one is paid at import time by every consumer, which is the crash the
|
|
257
|
+
* task above is about; a `await import('sharp')` inside the function that needs it is
|
|
258
|
+
* exactly the right shape and stays legal here.
|
|
259
|
+
*
|
|
260
|
+
* Limitation, stated rather than hidden: the externals are read out of the bundle's import
|
|
261
|
+
* statements textually, so an import bun emits across several lines would be missed. That
|
|
262
|
+
* makes this half a floor, not a ceiling — the runtime fixture above is the strict one.
|
|
263
|
+
*/
|
|
264
|
+
const NO_STATIC_OPTIONAL_PEERS = [
|
|
265
|
+
{
|
|
266
|
+
subpath: './storage',
|
|
267
|
+
/** Its `streamToken.ts` and (two hops out) `private-media/unlock.ts` both imported the
|
|
268
|
+
* `../auth` index for two jose functions, so `otplib` was static in here until 2.6.0. */
|
|
269
|
+
entry: 'src/server/storage/index.ts',
|
|
270
|
+
},
|
|
271
|
+
] as const;
|
|
272
|
+
|
|
273
|
+
const OPTIONAL_PEERS = new Set(
|
|
274
|
+
Object.entries(
|
|
275
|
+
(pkg as { peerDependenciesMeta?: Record<string, { optional?: boolean }> })
|
|
276
|
+
.peerDependenciesMeta ?? {},
|
|
277
|
+
)
|
|
278
|
+
.filter(([, meta]) => meta?.optional === true)
|
|
279
|
+
.map(([name]) => name),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
/** `import x from "pkg"`, `import "pkg"`, `export … from "pkg"` — one line each, as bun emits. */
|
|
283
|
+
const STATIC_SPECIFIER = /^(?:import|export)\s[^\n]*?["']([^"'\n]+)["'];?$|^import\s*["']([^"'\n]+)["'];?$/gm;
|
|
284
|
+
|
|
285
|
+
const staticOptionalPeersOf = (entry: string): string[] => {
|
|
286
|
+
const build = Bun.spawnSync(
|
|
287
|
+
['bun', 'build', entry, '--target=bun', '--packages=external'],
|
|
288
|
+
{ cwd: REPO, stdout: 'pipe', stderr: 'pipe' },
|
|
289
|
+
);
|
|
290
|
+
const bundle = build.stdout.toString();
|
|
291
|
+
if (build.exitCode !== 0 || bundle.trim() === '') {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`could not bundle ${entry} (exit ${build.exitCode}) — a check that cannot measure is not a passing check:\n${build.stderr.toString()}`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const found = new Set<string>();
|
|
297
|
+
for (const match of bundle.matchAll(STATIC_SPECIFIER)) {
|
|
298
|
+
const specifier = match[1] ?? match[2];
|
|
299
|
+
if (specifier === undefined || specifier.startsWith('.')) continue;
|
|
300
|
+
// `@scope/name/deep` → `@scope/name`, `pkg/deep` → `pkg`.
|
|
301
|
+
const parts = specifier.split('/');
|
|
302
|
+
const name = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
303
|
+
if (name !== undefined && OPTIONAL_PEERS.has(name)) found.add(name);
|
|
304
|
+
}
|
|
305
|
+
return [...found].sort();
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const outcomes = await run();
|
|
309
|
+
|
|
310
|
+
describe('leaf subpaths import nothing at runtime', () => {
|
|
311
|
+
it('has a fixture with no peers installed (negative control)', () => {
|
|
312
|
+
const control = outcomes.control;
|
|
313
|
+
expect(control, 'the control never ran — the fixture is not measuring anything').toBeDefined();
|
|
314
|
+
expect(
|
|
315
|
+
control?.ok === false,
|
|
316
|
+
`the fixture resolved 'otplib' — bun auto-installed it out of ~/.bun/install/cache, or a node_modules above the fixture answered. Every assertion below is vacuous until that is fixed; do not delete this test to make it pass.`,
|
|
317
|
+
).toBe(true);
|
|
318
|
+
expect(
|
|
319
|
+
control?.ok === false ? control.error : '',
|
|
320
|
+
'the control failed for the wrong reason — it must fail on the missing PACKAGE',
|
|
321
|
+
).toContain("Cannot find package 'otplib'");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
for (const leaf of LEAVES) {
|
|
325
|
+
it(`${pkg.name}/${leaf.subpath.slice(2)} imports clean`, () => {
|
|
326
|
+
const outcome = outcomes[leaf.subpath];
|
|
327
|
+
expect(outcome, `${leaf.subpath} was never imported by the fixture`).toBeDefined();
|
|
328
|
+
expect(
|
|
329
|
+
outcome?.ok === true ? '' : outcome?.ok === false ? outcome.error : 'missing',
|
|
330
|
+
`${pkg.name}/${leaf.subpath.slice(2)} must import NOTHING at runtime — that is the entire reason the subpath exists. Move whatever it now reaches behind \`import type\`, or split the leaf again; do not add the dependency to a consumer.`,
|
|
331
|
+
).toBe('');
|
|
332
|
+
// Non-vacuity: a module that resolved but exported nothing would otherwise pass.
|
|
333
|
+
expect(
|
|
334
|
+
outcome?.ok === true ? outcome.keys : [],
|
|
335
|
+
`${leaf.subpath} resolved but does not export ${leaf.evaluates} — the check is pointing at the wrong file`,
|
|
336
|
+
).toContain(leaf.evaluates);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('barrels reach no optional peer statically', () => {
|
|
342
|
+
it('knows which peers are optional', () => {
|
|
343
|
+
// Guards the assertions below: an empty set makes every one of them pass vacuously.
|
|
344
|
+
expect(OPTIONAL_PEERS.size, 'peerDependenciesMeta lists no optional peer').toBeGreaterThan(3);
|
|
345
|
+
expect([...OPTIONAL_PEERS], 'otplib is the peer this whole spec was written for').toContain(
|
|
346
|
+
'otplib',
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
for (const target of NO_STATIC_OPTIONAL_PEERS) {
|
|
351
|
+
it(`${pkg.name}/${target.subpath.slice(2)} costs a consumer no optional peer to import`, () => {
|
|
352
|
+
expect(
|
|
353
|
+
staticOptionalPeersOf(target.entry),
|
|
354
|
+
`importing ${pkg.name}/${target.subpath.slice(2)} statically pulls these OPTIONAL peers, so every consumer must install them for code it may never call. Either import the leaf module that has what you need instead of a sibling INDEX (that is how 'otplib' got in here twice), or make the dependency lazy: \`const { x } = await import('pkg')\` inside the function that needs it.`,
|
|
355
|
+
).toEqual([]);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|