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,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createPgSearchProvider` — the SAAS profile's ranked-retrieval tier (04 §3,
|
|
3
|
+
* audit gap L11 and frontier item 4(c)).
|
|
4
|
+
*
|
|
5
|
+
* Before this file, the saas profile had **no search tier at all**. `bootEngineApp`
|
|
6
|
+
* derived its `SearchProvider` from the local SQLite handles it does not open
|
|
7
|
+
* when a host injects storage, so under saas `search` was `undefined`, the
|
|
8
|
+
* `reindex` job fell through to the loud "needs a wired SearchProvider"
|
|
9
|
+
* placeholder, and `KnowledgeService.search` took its `LIKE` fallback for every
|
|
10
|
+
* tenant on the platform. 04 §1's gate text — "a leased `reindex` executing
|
|
11
|
+
* through the pg-dialect repo" — was unachievable, and the `genAppSaasSmoke`
|
|
12
|
+
* job leg asserted that the placeholder failed, as designed. This is the far
|
|
13
|
+
* side that makes both of those real.
|
|
14
|
+
*
|
|
15
|
+
* ## What is shared with the local provider, and what is not
|
|
16
|
+
*
|
|
17
|
+
* **The vector tier is shared, exactly.** Chunking, the little-endian embedding
|
|
18
|
+
* codec, the revision guard, the comparability predicates, the cosine rank and
|
|
19
|
+
* the reciprocal-rank fusion all come from `cwip`'s `searchVectorTier` — one
|
|
20
|
+
* implementation, driven here through a Postgres executor and there through a
|
|
21
|
+
* SQLite one. A second copy of ranking math is the worst kind of duplication:
|
|
22
|
+
* both copies keep returning plausible results after they diverge.
|
|
23
|
+
*
|
|
24
|
+
* **The keyword tier is NOT shared, and cannot be.** SQLite's is an FTS5
|
|
25
|
+
* external-content virtual table maintained by triggers; this one is a
|
|
26
|
+
* `tsvector` generated column with a GIN index. `domains/search.ts` already
|
|
27
|
+
* refuses to compile its chain for Postgres and says why. What IS held identical
|
|
28
|
+
* is the OBSERVABLE contract: the same 4× title weighting (`PG_RANK_WEIGHTS`
|
|
29
|
+
* against `bm25(search_fts, 4.0, 1.0)`), the same "higher score is better" sign,
|
|
30
|
+
* the same tenancy-as-a-predicate law, and the same behavior on a user typing
|
|
31
|
+
* punctuation into a search box. Two profiles that ranked one corpus differently
|
|
32
|
+
* would make "search ranks wrong in production" unreproducible on a laptop.
|
|
33
|
+
*
|
|
34
|
+
* ## Two things Postgres makes better, and one it makes worse
|
|
35
|
+
*
|
|
36
|
+
* **Better: a search box is not a query language, and here it is not a hazard
|
|
37
|
+
* either.** The SQLite provider catches an FTS5 syntax error and retries the
|
|
38
|
+
* whole input as a quoted phrase literal. `websearch_to_tsquery` cannot raise a
|
|
39
|
+
* syntax error on any input — quotes, `-`, `OR`, an unbalanced paren are all
|
|
40
|
+
* defined — so the retry has nothing to catch and does not exist. That is a real
|
|
41
|
+
* difference in the failure surface, not a stylistic one.
|
|
42
|
+
*
|
|
43
|
+
* **Better: the shadow cannot drift.** `tsv` is `GENERATED ALWAYS AS … STORED`,
|
|
44
|
+
* so no code path writes it — see `pgSearchSchema.ts`.
|
|
45
|
+
*
|
|
46
|
+
* **Worse: the connection accounting.** Every operation checks a connection out
|
|
47
|
+
* of the ONE pool, opens a transaction, scopes it to the tenant and returns it.
|
|
48
|
+
* The vector tier's chunk writes go through a per-statement scoped executor, so
|
|
49
|
+
* writing an eight-chunk document is eight checkouts rather than one. That is
|
|
50
|
+
* deliberate: the alternative is holding a pooled connection and an open
|
|
51
|
+
* transaction across eight embedding round-trips, which is how a search backlog
|
|
52
|
+
* takes an entire deployment's Postgres down. The chunk write is already
|
|
53
|
+
* dominated by inference latency; the checkout is noise beside it.
|
|
54
|
+
*
|
|
55
|
+
* ## The ceiling, again stated rather than hidden
|
|
56
|
+
*
|
|
57
|
+
* The vector rank is an exact cosine scan over a bounded candidate set, not an
|
|
58
|
+
* ANN index — no `pgvector`, no extension a customer's managed Postgres may
|
|
59
|
+
* refuse to install. `cwip`'s `searchVectorTier` header carries the numbers and
|
|
60
|
+
* the upgrade path; truncation is REPORTED through `onVectorTruncated` rather
|
|
61
|
+
* than swallowed, because a search tier that quietly degrades as a customer
|
|
62
|
+
* succeeds is worse than one that says so.
|
|
63
|
+
*/
|
|
64
|
+
import { type RebuildableSearchProvider } from 'cwip/search-provider';
|
|
65
|
+
import type { ChunkOptions } from 'cwip/search';
|
|
66
|
+
import { type SaasPool } from 'cwip/storage';
|
|
67
|
+
export interface PgSearchProviderOptions {
|
|
68
|
+
/** The deployment's ONE pool. Injected — this module opens no socket (law #8). */
|
|
69
|
+
pool: SaasPool;
|
|
70
|
+
defaultLimit?: number;
|
|
71
|
+
maxLimit?: number;
|
|
72
|
+
/**
|
|
73
|
+
* The embedder backing the vector tier. Absent ⇒ the provider has no `embed`
|
|
74
|
+
* method at all and `search_chunks` stays honestly empty — the same capability
|
|
75
|
+
* signal the local provider gives, so a caller's `provider.embed?.(…)` test
|
|
76
|
+
* means the same thing on both profiles.
|
|
77
|
+
*/
|
|
78
|
+
embed?(text: string, tenantId: string): Promise<Float32Array>;
|
|
79
|
+
/** Which model `embed` speaks for. Recorded per chunk; a query predicate. */
|
|
80
|
+
embedModel?: string;
|
|
81
|
+
chunkOptions?: ChunkOptions;
|
|
82
|
+
vectorCandidateLimit?: number;
|
|
83
|
+
onVectorTruncated?(detail: {
|
|
84
|
+
tenantId: string;
|
|
85
|
+
corpora: readonly string[];
|
|
86
|
+
scanned: number;
|
|
87
|
+
}): void;
|
|
88
|
+
/** Where a vector-tier failure goes. The keyword tier is the floor. */
|
|
89
|
+
onVectorError?(error: unknown, phase: 'index' | 'query'): void;
|
|
90
|
+
}
|
|
91
|
+
export declare function createPgSearchProvider(options: PgSearchProviderOptions): RebuildableSearchProvider;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createPgSearchProvider` — the SAAS profile's ranked-retrieval tier (04 §3,
|
|
3
|
+
* audit gap L11 and frontier item 4(c)).
|
|
4
|
+
*
|
|
5
|
+
* Before this file, the saas profile had **no search tier at all**. `bootEngineApp`
|
|
6
|
+
* derived its `SearchProvider` from the local SQLite handles it does not open
|
|
7
|
+
* when a host injects storage, so under saas `search` was `undefined`, the
|
|
8
|
+
* `reindex` job fell through to the loud "needs a wired SearchProvider"
|
|
9
|
+
* placeholder, and `KnowledgeService.search` took its `LIKE` fallback for every
|
|
10
|
+
* tenant on the platform. 04 §1's gate text — "a leased `reindex` executing
|
|
11
|
+
* through the pg-dialect repo" — was unachievable, and the `genAppSaasSmoke`
|
|
12
|
+
* job leg asserted that the placeholder failed, as designed. This is the far
|
|
13
|
+
* side that makes both of those real.
|
|
14
|
+
*
|
|
15
|
+
* ## What is shared with the local provider, and what is not
|
|
16
|
+
*
|
|
17
|
+
* **The vector tier is shared, exactly.** Chunking, the little-endian embedding
|
|
18
|
+
* codec, the revision guard, the comparability predicates, the cosine rank and
|
|
19
|
+
* the reciprocal-rank fusion all come from `cwip`'s `searchVectorTier` — one
|
|
20
|
+
* implementation, driven here through a Postgres executor and there through a
|
|
21
|
+
* SQLite one. A second copy of ranking math is the worst kind of duplication:
|
|
22
|
+
* both copies keep returning plausible results after they diverge.
|
|
23
|
+
*
|
|
24
|
+
* **The keyword tier is NOT shared, and cannot be.** SQLite's is an FTS5
|
|
25
|
+
* external-content virtual table maintained by triggers; this one is a
|
|
26
|
+
* `tsvector` generated column with a GIN index. `domains/search.ts` already
|
|
27
|
+
* refuses to compile its chain for Postgres and says why. What IS held identical
|
|
28
|
+
* is the OBSERVABLE contract: the same 4× title weighting (`PG_RANK_WEIGHTS`
|
|
29
|
+
* against `bm25(search_fts, 4.0, 1.0)`), the same "higher score is better" sign,
|
|
30
|
+
* the same tenancy-as-a-predicate law, and the same behavior on a user typing
|
|
31
|
+
* punctuation into a search box. Two profiles that ranked one corpus differently
|
|
32
|
+
* would make "search ranks wrong in production" unreproducible on a laptop.
|
|
33
|
+
*
|
|
34
|
+
* ## Two things Postgres makes better, and one it makes worse
|
|
35
|
+
*
|
|
36
|
+
* **Better: a search box is not a query language, and here it is not a hazard
|
|
37
|
+
* either.** The SQLite provider catches an FTS5 syntax error and retries the
|
|
38
|
+
* whole input as a quoted phrase literal. `websearch_to_tsquery` cannot raise a
|
|
39
|
+
* syntax error on any input — quotes, `-`, `OR`, an unbalanced paren are all
|
|
40
|
+
* defined — so the retry has nothing to catch and does not exist. That is a real
|
|
41
|
+
* difference in the failure surface, not a stylistic one.
|
|
42
|
+
*
|
|
43
|
+
* **Better: the shadow cannot drift.** `tsv` is `GENERATED ALWAYS AS … STORED`,
|
|
44
|
+
* so no code path writes it — see `pgSearchSchema.ts`.
|
|
45
|
+
*
|
|
46
|
+
* **Worse: the connection accounting.** Every operation checks a connection out
|
|
47
|
+
* of the ONE pool, opens a transaction, scopes it to the tenant and returns it.
|
|
48
|
+
* The vector tier's chunk writes go through a per-statement scoped executor, so
|
|
49
|
+
* writing an eight-chunk document is eight checkouts rather than one. That is
|
|
50
|
+
* deliberate: the alternative is holding a pooled connection and an open
|
|
51
|
+
* transaction across eight embedding round-trips, which is how a search backlog
|
|
52
|
+
* takes an entire deployment's Postgres down. The chunk write is already
|
|
53
|
+
* dominated by inference latency; the checkout is noise beside it.
|
|
54
|
+
*
|
|
55
|
+
* ## The ceiling, again stated rather than hidden
|
|
56
|
+
*
|
|
57
|
+
* The vector rank is an exact cosine scan over a bounded candidate set, not an
|
|
58
|
+
* ANN index — no `pgvector`, no extension a customer's managed Postgres may
|
|
59
|
+
* refuse to install. `cwip`'s `searchVectorTier` header carries the numbers and
|
|
60
|
+
* the upgrade path; truncation is REPORTED through `onVectorTruncated` rather
|
|
61
|
+
* than swallowed, because a search tier that quietly degrades as a customer
|
|
62
|
+
* succeeds is worse than one that says so.
|
|
63
|
+
*/
|
|
64
|
+
import { assertNonEmpty, clampLimit, clampPositive, countChunks, docKey, fuseSearchTiers, rankChunksByVector, UNKNOWN_EMBED_MODEL, writeDocChunks, } from 'cwip/search-provider';
|
|
65
|
+
import { createPostgresControlExecutor, saasSetTenantSql, } from 'cwip/storage';
|
|
66
|
+
import { AppError } from '../errors';
|
|
67
|
+
import { PG_RANK_WEIGHTS } from './pgSearchSchema';
|
|
68
|
+
const DEFAULT_LIMIT = 20;
|
|
69
|
+
const MAX_LIMIT = 200;
|
|
70
|
+
/** Mirrors the SQLite provider's fusion depth — see its `FUSION_DEPTH`. */
|
|
71
|
+
const FUSION_DEPTH = 4;
|
|
72
|
+
/** `ts_headline` window, in words. Comparable to the SQLite side's 14-token snippet. */
|
|
73
|
+
const SNIPPET_WORDS = 18;
|
|
74
|
+
export function createPgSearchProvider(options) {
|
|
75
|
+
const { pool } = options;
|
|
76
|
+
const defaultLimit = clampPositive(options.defaultLimit, DEFAULT_LIMIT);
|
|
77
|
+
const maxLimit = clampPositive(options.maxLimit, MAX_LIMIT);
|
|
78
|
+
const embedModel = options.embedModel ?? UNKNOWN_EMBED_MODEL;
|
|
79
|
+
/**
|
|
80
|
+
* Run `fn` on a connection scoped to `tenantId` for the duration of ONE
|
|
81
|
+
* transaction.
|
|
82
|
+
*
|
|
83
|
+
* The `BEGIN` is not decoration around a single statement: `set_config(…, true)`
|
|
84
|
+
* is transaction-local, and outside a transaction it would apply to the
|
|
85
|
+
* session and ride the connection back into the pool with another tenant's
|
|
86
|
+
* scope still set. That is the exact leak RLS exists to prevent, so the
|
|
87
|
+
* transaction is what makes the scoping safe rather than what makes it atomic.
|
|
88
|
+
*/
|
|
89
|
+
const scoped = async (tenantId, fn) => {
|
|
90
|
+
assertNonEmpty(tenantId, 'tenantId');
|
|
91
|
+
return await pool.withConnection(async (connection) => {
|
|
92
|
+
await connection.execute('BEGIN');
|
|
93
|
+
try {
|
|
94
|
+
const setTenant = saasSetTenantSql(tenantId);
|
|
95
|
+
await connection.execute(setTenant.sql, setTenant.params);
|
|
96
|
+
const result = await fn(createPostgresControlExecutor(connection));
|
|
97
|
+
await connection.execute('COMMIT');
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
// Best-effort: a connection already broken by the failure cannot roll
|
|
102
|
+
// back, and masking the original error with the rollback's would hide
|
|
103
|
+
// the thing worth reading.
|
|
104
|
+
await connection.execute('ROLLBACK').catch(() => undefined);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* An executor whose every statement is its own scoped transaction.
|
|
111
|
+
*
|
|
112
|
+
* This is what the vector tier runs on, and the shape is the point: the tier
|
|
113
|
+
* interleaves database writes with embedding round-trips, so a single
|
|
114
|
+
* long-lived transaction across it would pin a pooled connection for the
|
|
115
|
+
* duration of somebody else's inference queue. Each statement being separately
|
|
116
|
+
* atomic is also exactly what the tier's revision guard assumes — the guard is
|
|
117
|
+
* a `WHERE EXISTS` evaluated at write time, never a read-then-act.
|
|
118
|
+
*/
|
|
119
|
+
const perStatementExecutor = (tenantId) => ({
|
|
120
|
+
dialect: 'postgres',
|
|
121
|
+
all: (statement) => scoped(tenantId, (exec) => exec.all(statement)),
|
|
122
|
+
get: (statement) => scoped(tenantId, (exec) => exec.get(statement)),
|
|
123
|
+
write: (statement) => scoped(tenantId, (exec) => exec.write(statement)),
|
|
124
|
+
});
|
|
125
|
+
const keywordQuery = (query, tenantId, corpora, limit) => {
|
|
126
|
+
const where = ['d.tenant_id = ?', "d.tsv @@ websearch_to_tsquery('simple', ?)"];
|
|
127
|
+
const params = [query, query, tenantId, query];
|
|
128
|
+
if (corpora.length > 0) {
|
|
129
|
+
where.push(`d.corpus IN (${corpora.map(() => '?').join(', ')})`);
|
|
130
|
+
params.push(...corpora);
|
|
131
|
+
}
|
|
132
|
+
params.push(limit);
|
|
133
|
+
return {
|
|
134
|
+
// `ts_headline` gets the same empty start/stop markers the SQLite
|
|
135
|
+
// `snippet()` call uses, so a client rendering a hit does not have to know
|
|
136
|
+
// which profile produced it.
|
|
137
|
+
sql: `SELECT d.source_id AS id,
|
|
138
|
+
d.corpus AS corpus,
|
|
139
|
+
d.tenant_id AS tenant_id,
|
|
140
|
+
d.title AS title,
|
|
141
|
+
ts_headline('simple', d.body, websearch_to_tsquery('simple', ?),
|
|
142
|
+
'MaxWords=${SNIPPET_WORDS}, MinWords=1, ShortWord=1, HighlightAll=false, MaxFragments=1, StartSel="", StopSel=""') AS snippet,
|
|
143
|
+
ts_rank('${PG_RANK_WEIGHTS}'::float4[], d.tsv, websearch_to_tsquery('simple', ?)) AS rank
|
|
144
|
+
FROM search_docs d
|
|
145
|
+
WHERE ${where.join(' AND ')}
|
|
146
|
+
ORDER BY rank DESC, d.id DESC
|
|
147
|
+
LIMIT ?`,
|
|
148
|
+
params,
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
async now() {
|
|
153
|
+
// The INDEX's clock, in the index's own format — the same law the SQLite
|
|
154
|
+
// provider states. Comparing a JS `Date` against a value the database
|
|
155
|
+
// wrote is a format coupling nobody remembers is there.
|
|
156
|
+
const row = await pool.withConnection(async (connection) => createPostgresControlExecutor(connection).get({ sql: 'SELECT {{now}} AS now' }));
|
|
157
|
+
if (row === null)
|
|
158
|
+
throw new AppError('The search index clock returned no row.', {
|
|
159
|
+
code: 'SEARCH_CLOCK_UNAVAILABLE',
|
|
160
|
+
status: 500,
|
|
161
|
+
});
|
|
162
|
+
return row.now;
|
|
163
|
+
},
|
|
164
|
+
...(options.embed ? { embed: options.embed } : {}),
|
|
165
|
+
async index(doc) {
|
|
166
|
+
assertNonEmpty(doc.corpus, 'corpus');
|
|
167
|
+
assertNonEmpty(doc.id, 'id');
|
|
168
|
+
assertNonEmpty(doc.tenantId, 'tenantId');
|
|
169
|
+
// The document lands first and alone — the keyword tier is the floor, and
|
|
170
|
+
// an embedding round-trip is not something to hold a write lock across.
|
|
171
|
+
const identity = await scoped(doc.tenantId, async (executor) => {
|
|
172
|
+
const row = await executor.get({
|
|
173
|
+
sql: `INSERT INTO search_docs (corpus, source_id, tenant_id, user_id, title, body, rev, indexed_at)
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, {{now}})
|
|
175
|
+
ON CONFLICT (corpus, source_id, tenant_id) DO UPDATE SET
|
|
176
|
+
user_id = excluded.user_id,
|
|
177
|
+
title = excluded.title,
|
|
178
|
+
body = excluded.body,
|
|
179
|
+
rev = search_docs.rev + 1,
|
|
180
|
+
indexed_at = excluded.indexed_at
|
|
181
|
+
RETURNING id, rev`,
|
|
182
|
+
params: [doc.corpus, doc.id, doc.tenantId, null, doc.title, doc.body],
|
|
183
|
+
});
|
|
184
|
+
if (row === null)
|
|
185
|
+
throw new AppError('The search index upsert returned no row, so the document id is unknown.', {
|
|
186
|
+
code: 'SEARCH_UPSERT_EMPTY',
|
|
187
|
+
status: 500,
|
|
188
|
+
context: { corpus: doc.corpus, id: doc.id },
|
|
189
|
+
});
|
|
190
|
+
return row;
|
|
191
|
+
});
|
|
192
|
+
if (!options.embed)
|
|
193
|
+
return;
|
|
194
|
+
const embed = options.embed;
|
|
195
|
+
try {
|
|
196
|
+
await writeDocChunks(perStatementExecutor(doc.tenantId), {
|
|
197
|
+
// A pg `bigint` arrives as a string; it is an opaque key here either way.
|
|
198
|
+
docId: String(identity.id),
|
|
199
|
+
rev: Number(identity.rev),
|
|
200
|
+
text: doc.title ? `${doc.title}\n\n${doc.body}` : doc.body,
|
|
201
|
+
tenantId: doc.tenantId,
|
|
202
|
+
embed: (text, tenantId) => embed(text, tenantId),
|
|
203
|
+
model: embedModel,
|
|
204
|
+
...(options.chunkOptions ? { chunkOptions: options.chunkOptions } : {}),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
// Degrade, do not fail — see the SQLite provider's identical rule. The
|
|
209
|
+
// document IS indexed; failing here would make an embedder outage look
|
|
210
|
+
// like a write failure to the CDC tap, which retries.
|
|
211
|
+
options.onVectorError?.(error, 'index');
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
async remove(corpus, id, tenantId) {
|
|
215
|
+
// ONE transaction for both tables: an interrupted removal that dropped the
|
|
216
|
+
// document but kept its chunks would leave the vector tier ranking a
|
|
217
|
+
// document that no longer exists. (The FK cascade would handle it, but the
|
|
218
|
+
// explicit delete keeps the two profiles' behavior identical rather than
|
|
219
|
+
// resting on a schema detail one of them does not have.)
|
|
220
|
+
await scoped(tenantId, async (executor) => {
|
|
221
|
+
await executor.write({
|
|
222
|
+
sql: `DELETE FROM search_chunks WHERE doc_id IN (
|
|
223
|
+
SELECT id FROM search_docs WHERE corpus = ? AND source_id = ? AND tenant_id = ?)`,
|
|
224
|
+
params: [corpus, id, tenantId],
|
|
225
|
+
});
|
|
226
|
+
await executor.write({
|
|
227
|
+
sql: 'DELETE FROM search_docs WHERE corpus = ? AND source_id = ? AND tenant_id = ?',
|
|
228
|
+
params: [corpus, id, tenantId],
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
async query(q, opts) {
|
|
233
|
+
assertNonEmpty(opts.tenantId, 'tenantId');
|
|
234
|
+
const trimmed = q.trim();
|
|
235
|
+
if (trimmed.length === 0)
|
|
236
|
+
return [];
|
|
237
|
+
const corpora = opts.corpus ? [...opts.corpus] : [];
|
|
238
|
+
const limit = clampLimit(opts.limit, defaultLimit, maxLimit);
|
|
239
|
+
const tierLimit = options.embed ? Math.min(maxLimit, limit * FUSION_DEPTH) : limit;
|
|
240
|
+
const rows = await scoped(opts.tenantId, (executor) => executor.all(keywordQuery(trimmed, opts.tenantId, corpora, tierLimit)));
|
|
241
|
+
const keywordHits = rows.map((row) => ({
|
|
242
|
+
id: row.id,
|
|
243
|
+
corpus: row.corpus,
|
|
244
|
+
tenantId: row.tenant_id,
|
|
245
|
+
// `ts_rank` is already higher-is-better, which is the interface's
|
|
246
|
+
// contract — so unlike BM25 there is no sign to flip. Said explicitly
|
|
247
|
+
// because "flip it, the other one does" is the mistake available here.
|
|
248
|
+
score: Number(row.rank),
|
|
249
|
+
title: row.title,
|
|
250
|
+
snippet: row.snippet ?? '',
|
|
251
|
+
}));
|
|
252
|
+
if (!options.embed)
|
|
253
|
+
return keywordHits.slice(0, limit);
|
|
254
|
+
let vector;
|
|
255
|
+
try {
|
|
256
|
+
vector = await rankChunksByVector(perStatementExecutor(opts.tenantId), {
|
|
257
|
+
tenantId: opts.tenantId,
|
|
258
|
+
corpora,
|
|
259
|
+
queryVector: await options.embed(trimmed, opts.tenantId),
|
|
260
|
+
model: embedModel,
|
|
261
|
+
limit: tierLimit,
|
|
262
|
+
...(options.vectorCandidateLimit !== undefined
|
|
263
|
+
? { candidateLimit: options.vectorCandidateLimit }
|
|
264
|
+
: {}),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
options.onVectorError?.(error, 'query');
|
|
269
|
+
return keywordHits.slice(0, limit);
|
|
270
|
+
}
|
|
271
|
+
if (vector.truncated)
|
|
272
|
+
options.onVectorTruncated?.({ tenantId: opts.tenantId, corpora, scanned: vector.scanned });
|
|
273
|
+
return await fuseSearchTiers({
|
|
274
|
+
keyword: keywordHits,
|
|
275
|
+
vector: vector.chunks,
|
|
276
|
+
limit,
|
|
277
|
+
tenantId: opts.tenantId,
|
|
278
|
+
hydrate: async (keys) => {
|
|
279
|
+
// ONE statement for every missing title. A per-document lookup here is
|
|
280
|
+
// an N+1 on the hot path, and each of those N is a pool checkout.
|
|
281
|
+
const placeholders = keys.map(() => '(?, ?)').join(', ');
|
|
282
|
+
const params = keys.flatMap((key) => [key.corpus, key.docId]);
|
|
283
|
+
const found = await scoped(opts.tenantId, (executor) => executor.all({
|
|
284
|
+
sql: `SELECT corpus, source_id, title FROM search_docs
|
|
285
|
+
WHERE tenant_id = ? AND (corpus, source_id) IN (${placeholders})`,
|
|
286
|
+
params: [opts.tenantId, ...params],
|
|
287
|
+
}));
|
|
288
|
+
return new Map(found.map((row) => [docKey(row.corpus, row.source_id), { title: row.title }]));
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
async sweepStale(corpus, tenantId, epoch) {
|
|
293
|
+
assertNonEmpty(corpus, 'corpus');
|
|
294
|
+
assertNonEmpty(tenantId, 'tenantId');
|
|
295
|
+
assertNonEmpty(epoch, 'epoch');
|
|
296
|
+
return await scoped(tenantId, async (executor) => {
|
|
297
|
+
// Counted with its own statement rather than read off the DELETE's row
|
|
298
|
+
// count, so the number the reindex job logs is documents and not
|
|
299
|
+
// documents-plus-cascaded-chunks. The SQLite provider counts separately
|
|
300
|
+
// for the same reason (its FTS triggers inflate `changes`).
|
|
301
|
+
const stale = await executor.get({
|
|
302
|
+
sql: 'SELECT COUNT(*) AS n FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?',
|
|
303
|
+
params: [corpus, tenantId, epoch],
|
|
304
|
+
});
|
|
305
|
+
await executor.write({
|
|
306
|
+
sql: `DELETE FROM search_chunks WHERE doc_id IN (
|
|
307
|
+
SELECT id FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?)`,
|
|
308
|
+
params: [corpus, tenantId, epoch],
|
|
309
|
+
});
|
|
310
|
+
await executor.write({
|
|
311
|
+
sql: 'DELETE FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?',
|
|
312
|
+
params: [corpus, tenantId, epoch],
|
|
313
|
+
});
|
|
314
|
+
return Number(stale?.n ?? 0);
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
async count(corpus, tenantId) {
|
|
318
|
+
return await scoped(tenantId, async (executor) => {
|
|
319
|
+
const row = await executor.get({
|
|
320
|
+
sql: 'SELECT COUNT(*) AS n FROM search_docs WHERE corpus = ? AND tenant_id = ?',
|
|
321
|
+
params: [corpus, tenantId],
|
|
322
|
+
});
|
|
323
|
+
return Number(row?.n ?? 0);
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
async countChunks(corpus, tenantId) {
|
|
327
|
+
return await scoped(tenantId, (executor) => countChunks(executor, corpus, tenantId));
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|