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,421 @@
|
|
|
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 {
|
|
65
|
+
assertNonEmpty,
|
|
66
|
+
clampLimit,
|
|
67
|
+
clampPositive,
|
|
68
|
+
countChunks,
|
|
69
|
+
docKey,
|
|
70
|
+
fuseSearchTiers,
|
|
71
|
+
rankChunksByVector,
|
|
72
|
+
UNKNOWN_EMBED_MODEL,
|
|
73
|
+
writeDocChunks,
|
|
74
|
+
type RebuildableSearchProvider,
|
|
75
|
+
type SearchDoc,
|
|
76
|
+
type SearchHit,
|
|
77
|
+
type SearchQueryOptions,
|
|
78
|
+
type VectorRankResult,
|
|
79
|
+
} from 'cwip/search-provider';
|
|
80
|
+
import type { ChunkOptions } from 'cwip/search';
|
|
81
|
+
import {
|
|
82
|
+
createPostgresControlExecutor,
|
|
83
|
+
saasSetTenantSql,
|
|
84
|
+
type SaasConnection,
|
|
85
|
+
type SaasPool,
|
|
86
|
+
type Statement,
|
|
87
|
+
type StatementExecutor,
|
|
88
|
+
type WriteResult,
|
|
89
|
+
} from 'cwip/storage';
|
|
90
|
+
import { AppError } from '../errors';
|
|
91
|
+
import { PG_RANK_WEIGHTS } from './pgSearchSchema';
|
|
92
|
+
|
|
93
|
+
const DEFAULT_LIMIT = 20;
|
|
94
|
+
const MAX_LIMIT = 200;
|
|
95
|
+
/** Mirrors the SQLite provider's fusion depth — see its `FUSION_DEPTH`. */
|
|
96
|
+
const FUSION_DEPTH = 4;
|
|
97
|
+
/** `ts_headline` window, in words. Comparable to the SQLite side's 14-token snippet. */
|
|
98
|
+
const SNIPPET_WORDS = 18;
|
|
99
|
+
|
|
100
|
+
export interface PgSearchProviderOptions {
|
|
101
|
+
/** The deployment's ONE pool. Injected — this module opens no socket (law #8). */
|
|
102
|
+
pool: SaasPool;
|
|
103
|
+
defaultLimit?: number;
|
|
104
|
+
maxLimit?: number;
|
|
105
|
+
/**
|
|
106
|
+
* The embedder backing the vector tier. Absent ⇒ the provider has no `embed`
|
|
107
|
+
* method at all and `search_chunks` stays honestly empty — the same capability
|
|
108
|
+
* signal the local provider gives, so a caller's `provider.embed?.(…)` test
|
|
109
|
+
* means the same thing on both profiles.
|
|
110
|
+
*/
|
|
111
|
+
embed?(text: string, tenantId: string): Promise<Float32Array>;
|
|
112
|
+
/** Which model `embed` speaks for. Recorded per chunk; a query predicate. */
|
|
113
|
+
embedModel?: string;
|
|
114
|
+
chunkOptions?: ChunkOptions;
|
|
115
|
+
vectorCandidateLimit?: number;
|
|
116
|
+
onVectorTruncated?(detail: { tenantId: string; corpora: readonly string[]; scanned: number }): void;
|
|
117
|
+
/** Where a vector-tier failure goes. The keyword tier is the floor. */
|
|
118
|
+
onVectorError?(error: unknown, phase: 'index' | 'query'): void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface HitRow {
|
|
122
|
+
id: string;
|
|
123
|
+
corpus: string;
|
|
124
|
+
tenant_id: string;
|
|
125
|
+
title: string;
|
|
126
|
+
snippet: string | null;
|
|
127
|
+
rank: number | string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface DocIdentity {
|
|
131
|
+
id: string | number;
|
|
132
|
+
rev: number | string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface DocMetaRow {
|
|
136
|
+
corpus: string;
|
|
137
|
+
source_id: string;
|
|
138
|
+
title: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function createPgSearchProvider(options: PgSearchProviderOptions): RebuildableSearchProvider {
|
|
142
|
+
const { pool } = options;
|
|
143
|
+
const defaultLimit = clampPositive(options.defaultLimit, DEFAULT_LIMIT);
|
|
144
|
+
const maxLimit = clampPositive(options.maxLimit, MAX_LIMIT);
|
|
145
|
+
const embedModel = options.embedModel ?? UNKNOWN_EMBED_MODEL;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Run `fn` on a connection scoped to `tenantId` for the duration of ONE
|
|
149
|
+
* transaction.
|
|
150
|
+
*
|
|
151
|
+
* The `BEGIN` is not decoration around a single statement: `set_config(…, true)`
|
|
152
|
+
* is transaction-local, and outside a transaction it would apply to the
|
|
153
|
+
* session and ride the connection back into the pool with another tenant's
|
|
154
|
+
* scope still set. That is the exact leak RLS exists to prevent, so the
|
|
155
|
+
* transaction is what makes the scoping safe rather than what makes it atomic.
|
|
156
|
+
*/
|
|
157
|
+
const scoped = async <T>(tenantId: string, fn: (executor: StatementExecutor) => Promise<T>): Promise<T> => {
|
|
158
|
+
assertNonEmpty(tenantId, 'tenantId');
|
|
159
|
+
return await pool.withConnection(async (connection: SaasConnection) => {
|
|
160
|
+
await connection.execute('BEGIN');
|
|
161
|
+
try {
|
|
162
|
+
const setTenant = saasSetTenantSql(tenantId);
|
|
163
|
+
await connection.execute(setTenant.sql, setTenant.params);
|
|
164
|
+
const result = await fn(createPostgresControlExecutor(connection));
|
|
165
|
+
await connection.execute('COMMIT');
|
|
166
|
+
return result;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
// Best-effort: a connection already broken by the failure cannot roll
|
|
169
|
+
// back, and masking the original error with the rollback's would hide
|
|
170
|
+
// the thing worth reading.
|
|
171
|
+
await connection.execute('ROLLBACK').catch(() => undefined);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* An executor whose every statement is its own scoped transaction.
|
|
179
|
+
*
|
|
180
|
+
* This is what the vector tier runs on, and the shape is the point: the tier
|
|
181
|
+
* interleaves database writes with embedding round-trips, so a single
|
|
182
|
+
* long-lived transaction across it would pin a pooled connection for the
|
|
183
|
+
* duration of somebody else's inference queue. Each statement being separately
|
|
184
|
+
* atomic is also exactly what the tier's revision guard assumes — the guard is
|
|
185
|
+
* a `WHERE EXISTS` evaluated at write time, never a read-then-act.
|
|
186
|
+
*/
|
|
187
|
+
const perStatementExecutor = (tenantId: string): StatementExecutor => ({
|
|
188
|
+
dialect: 'postgres',
|
|
189
|
+
all: <Row>(statement: Statement): Promise<Row[]> => scoped(tenantId, (exec) => exec.all<Row>(statement)),
|
|
190
|
+
get: <Row>(statement: Statement): Promise<Row | null> => scoped(tenantId, (exec) => exec.get<Row>(statement)),
|
|
191
|
+
write: <Row>(statement: Statement): Promise<WriteResult<Row>> =>
|
|
192
|
+
scoped(tenantId, (exec) => exec.write<Row>(statement)),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const keywordQuery = (
|
|
196
|
+
query: string,
|
|
197
|
+
tenantId: string,
|
|
198
|
+
corpora: readonly string[],
|
|
199
|
+
limit: number,
|
|
200
|
+
): Statement => {
|
|
201
|
+
const where = ['d.tenant_id = ?', "d.tsv @@ websearch_to_tsquery('simple', ?)"];
|
|
202
|
+
const params: unknown[] = [query, query, tenantId, query];
|
|
203
|
+
if (corpora.length > 0) {
|
|
204
|
+
where.push(`d.corpus IN (${corpora.map(() => '?').join(', ')})`);
|
|
205
|
+
params.push(...corpora);
|
|
206
|
+
}
|
|
207
|
+
params.push(limit);
|
|
208
|
+
return {
|
|
209
|
+
// `ts_headline` gets the same empty start/stop markers the SQLite
|
|
210
|
+
// `snippet()` call uses, so a client rendering a hit does not have to know
|
|
211
|
+
// which profile produced it.
|
|
212
|
+
sql: `SELECT d.source_id AS id,
|
|
213
|
+
d.corpus AS corpus,
|
|
214
|
+
d.tenant_id AS tenant_id,
|
|
215
|
+
d.title AS title,
|
|
216
|
+
ts_headline('simple', d.body, websearch_to_tsquery('simple', ?),
|
|
217
|
+
'MaxWords=${SNIPPET_WORDS}, MinWords=1, ShortWord=1, HighlightAll=false, MaxFragments=1, StartSel="", StopSel=""') AS snippet,
|
|
218
|
+
ts_rank('${PG_RANK_WEIGHTS}'::float4[], d.tsv, websearch_to_tsquery('simple', ?)) AS rank
|
|
219
|
+
FROM search_docs d
|
|
220
|
+
WHERE ${where.join(' AND ')}
|
|
221
|
+
ORDER BY rank DESC, d.id DESC
|
|
222
|
+
LIMIT ?`,
|
|
223
|
+
params,
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
async now(): Promise<string> {
|
|
229
|
+
// The INDEX's clock, in the index's own format — the same law the SQLite
|
|
230
|
+
// provider states. Comparing a JS `Date` against a value the database
|
|
231
|
+
// wrote is a format coupling nobody remembers is there.
|
|
232
|
+
const row = await pool.withConnection(async (connection) =>
|
|
233
|
+
createPostgresControlExecutor(connection).get<{ now: string }>({ sql: 'SELECT {{now}} AS now' }),
|
|
234
|
+
);
|
|
235
|
+
if (row === null)
|
|
236
|
+
throw new AppError('The search index clock returned no row.', {
|
|
237
|
+
code: 'SEARCH_CLOCK_UNAVAILABLE',
|
|
238
|
+
status: 500,
|
|
239
|
+
});
|
|
240
|
+
return row.now;
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
...(options.embed ? { embed: options.embed } : {}),
|
|
244
|
+
|
|
245
|
+
async index(doc: SearchDoc): Promise<void> {
|
|
246
|
+
assertNonEmpty(doc.corpus, 'corpus');
|
|
247
|
+
assertNonEmpty(doc.id, 'id');
|
|
248
|
+
assertNonEmpty(doc.tenantId, 'tenantId');
|
|
249
|
+
|
|
250
|
+
// The document lands first and alone — the keyword tier is the floor, and
|
|
251
|
+
// an embedding round-trip is not something to hold a write lock across.
|
|
252
|
+
const identity = await scoped(doc.tenantId, async (executor) => {
|
|
253
|
+
const row = await executor.get<DocIdentity>({
|
|
254
|
+
sql: `INSERT INTO search_docs (corpus, source_id, tenant_id, user_id, title, body, rev, indexed_at)
|
|
255
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, {{now}})
|
|
256
|
+
ON CONFLICT (corpus, source_id, tenant_id) DO UPDATE SET
|
|
257
|
+
user_id = excluded.user_id,
|
|
258
|
+
title = excluded.title,
|
|
259
|
+
body = excluded.body,
|
|
260
|
+
rev = search_docs.rev + 1,
|
|
261
|
+
indexed_at = excluded.indexed_at
|
|
262
|
+
RETURNING id, rev`,
|
|
263
|
+
params: [doc.corpus, doc.id, doc.tenantId, null, doc.title, doc.body],
|
|
264
|
+
});
|
|
265
|
+
if (row === null)
|
|
266
|
+
throw new AppError('The search index upsert returned no row, so the document id is unknown.', {
|
|
267
|
+
code: 'SEARCH_UPSERT_EMPTY',
|
|
268
|
+
status: 500,
|
|
269
|
+
context: { corpus: doc.corpus, id: doc.id },
|
|
270
|
+
});
|
|
271
|
+
return row;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (!options.embed) return;
|
|
275
|
+
const embed = options.embed;
|
|
276
|
+
try {
|
|
277
|
+
await writeDocChunks(perStatementExecutor(doc.tenantId), {
|
|
278
|
+
// A pg `bigint` arrives as a string; it is an opaque key here either way.
|
|
279
|
+
docId: String(identity.id),
|
|
280
|
+
rev: Number(identity.rev),
|
|
281
|
+
text: doc.title ? `${doc.title}\n\n${doc.body}` : doc.body,
|
|
282
|
+
tenantId: doc.tenantId,
|
|
283
|
+
embed: (text, tenantId) => embed(text, tenantId),
|
|
284
|
+
model: embedModel,
|
|
285
|
+
...(options.chunkOptions ? { chunkOptions: options.chunkOptions } : {}),
|
|
286
|
+
});
|
|
287
|
+
} catch (error) {
|
|
288
|
+
// Degrade, do not fail — see the SQLite provider's identical rule. The
|
|
289
|
+
// document IS indexed; failing here would make an embedder outage look
|
|
290
|
+
// like a write failure to the CDC tap, which retries.
|
|
291
|
+
options.onVectorError?.(error, 'index');
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
async remove(corpus: string, id: string, tenantId: string): Promise<void> {
|
|
296
|
+
// ONE transaction for both tables: an interrupted removal that dropped the
|
|
297
|
+
// document but kept its chunks would leave the vector tier ranking a
|
|
298
|
+
// document that no longer exists. (The FK cascade would handle it, but the
|
|
299
|
+
// explicit delete keeps the two profiles' behavior identical rather than
|
|
300
|
+
// resting on a schema detail one of them does not have.)
|
|
301
|
+
await scoped(tenantId, async (executor) => {
|
|
302
|
+
await executor.write({
|
|
303
|
+
sql: `DELETE FROM search_chunks WHERE doc_id IN (
|
|
304
|
+
SELECT id FROM search_docs WHERE corpus = ? AND source_id = ? AND tenant_id = ?)`,
|
|
305
|
+
params: [corpus, id, tenantId],
|
|
306
|
+
});
|
|
307
|
+
await executor.write({
|
|
308
|
+
sql: 'DELETE FROM search_docs WHERE corpus = ? AND source_id = ? AND tenant_id = ?',
|
|
309
|
+
params: [corpus, id, tenantId],
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
async query(q: string, opts: SearchQueryOptions): Promise<SearchHit[]> {
|
|
315
|
+
assertNonEmpty(opts.tenantId, 'tenantId');
|
|
316
|
+
const trimmed = q.trim();
|
|
317
|
+
if (trimmed.length === 0) return [];
|
|
318
|
+
|
|
319
|
+
const corpora = opts.corpus ? [...opts.corpus] : [];
|
|
320
|
+
const limit = clampLimit(opts.limit, defaultLimit, maxLimit);
|
|
321
|
+
const tierLimit = options.embed ? Math.min(maxLimit, limit * FUSION_DEPTH) : limit;
|
|
322
|
+
|
|
323
|
+
const rows = await scoped(opts.tenantId, (executor) =>
|
|
324
|
+
executor.all<HitRow>(keywordQuery(trimmed, opts.tenantId, corpora, tierLimit)),
|
|
325
|
+
);
|
|
326
|
+
const keywordHits: SearchHit[] = rows.map((row) => ({
|
|
327
|
+
id: row.id,
|
|
328
|
+
corpus: row.corpus,
|
|
329
|
+
tenantId: row.tenant_id,
|
|
330
|
+
// `ts_rank` is already higher-is-better, which is the interface's
|
|
331
|
+
// contract — so unlike BM25 there is no sign to flip. Said explicitly
|
|
332
|
+
// because "flip it, the other one does" is the mistake available here.
|
|
333
|
+
score: Number(row.rank),
|
|
334
|
+
title: row.title,
|
|
335
|
+
snippet: row.snippet ?? '',
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
if (!options.embed) return keywordHits.slice(0, limit);
|
|
339
|
+
|
|
340
|
+
let vector: VectorRankResult;
|
|
341
|
+
try {
|
|
342
|
+
vector = await rankChunksByVector(perStatementExecutor(opts.tenantId), {
|
|
343
|
+
tenantId: opts.tenantId,
|
|
344
|
+
corpora,
|
|
345
|
+
queryVector: await options.embed(trimmed, opts.tenantId),
|
|
346
|
+
model: embedModel,
|
|
347
|
+
limit: tierLimit,
|
|
348
|
+
...(options.vectorCandidateLimit !== undefined
|
|
349
|
+
? { candidateLimit: options.vectorCandidateLimit }
|
|
350
|
+
: {}),
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
options.onVectorError?.(error, 'query');
|
|
354
|
+
return keywordHits.slice(0, limit);
|
|
355
|
+
}
|
|
356
|
+
if (vector.truncated)
|
|
357
|
+
options.onVectorTruncated?.({ tenantId: opts.tenantId, corpora, scanned: vector.scanned });
|
|
358
|
+
|
|
359
|
+
return await fuseSearchTiers({
|
|
360
|
+
keyword: keywordHits,
|
|
361
|
+
vector: vector.chunks,
|
|
362
|
+
limit,
|
|
363
|
+
tenantId: opts.tenantId,
|
|
364
|
+
hydrate: async (keys) => {
|
|
365
|
+
// ONE statement for every missing title. A per-document lookup here is
|
|
366
|
+
// an N+1 on the hot path, and each of those N is a pool checkout.
|
|
367
|
+
const placeholders = keys.map(() => '(?, ?)').join(', ');
|
|
368
|
+
const params = keys.flatMap((key) => [key.corpus, key.docId]);
|
|
369
|
+
const found = await scoped(opts.tenantId, (executor) =>
|
|
370
|
+
executor.all<DocMetaRow>({
|
|
371
|
+
sql: `SELECT corpus, source_id, title FROM search_docs
|
|
372
|
+
WHERE tenant_id = ? AND (corpus, source_id) IN (${placeholders})`,
|
|
373
|
+
params: [opts.tenantId, ...params],
|
|
374
|
+
}),
|
|
375
|
+
);
|
|
376
|
+
return new Map(found.map((row) => [docKey(row.corpus, row.source_id), { title: row.title }]));
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
async sweepStale(corpus: string, tenantId: string, epoch: string): Promise<number> {
|
|
382
|
+
assertNonEmpty(corpus, 'corpus');
|
|
383
|
+
assertNonEmpty(tenantId, 'tenantId');
|
|
384
|
+
assertNonEmpty(epoch, 'epoch');
|
|
385
|
+
return await scoped(tenantId, async (executor) => {
|
|
386
|
+
// Counted with its own statement rather than read off the DELETE's row
|
|
387
|
+
// count, so the number the reindex job logs is documents and not
|
|
388
|
+
// documents-plus-cascaded-chunks. The SQLite provider counts separately
|
|
389
|
+
// for the same reason (its FTS triggers inflate `changes`).
|
|
390
|
+
const stale = await executor.get<{ n: number | string }>({
|
|
391
|
+
sql: 'SELECT COUNT(*) AS n FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?',
|
|
392
|
+
params: [corpus, tenantId, epoch],
|
|
393
|
+
});
|
|
394
|
+
await executor.write({
|
|
395
|
+
sql: `DELETE FROM search_chunks WHERE doc_id IN (
|
|
396
|
+
SELECT id FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?)`,
|
|
397
|
+
params: [corpus, tenantId, epoch],
|
|
398
|
+
});
|
|
399
|
+
await executor.write({
|
|
400
|
+
sql: 'DELETE FROM search_docs WHERE corpus = ? AND tenant_id = ? AND indexed_at < ?',
|
|
401
|
+
params: [corpus, tenantId, epoch],
|
|
402
|
+
});
|
|
403
|
+
return Number(stale?.n ?? 0);
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
|
|
407
|
+
async count(corpus: string, tenantId: string): Promise<number> {
|
|
408
|
+
return await scoped(tenantId, async (executor) => {
|
|
409
|
+
const row = await executor.get<{ n: number | string }>({
|
|
410
|
+
sql: 'SELECT COUNT(*) AS n FROM search_docs WHERE corpus = ? AND tenant_id = ?',
|
|
411
|
+
params: [corpus, tenantId],
|
|
412
|
+
});
|
|
413
|
+
return Number(row?.n ?? 0);
|
|
414
|
+
});
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
async countChunks(corpus: string, tenantId: string): Promise<number> {
|
|
418
|
+
return await scoped(tenantId, (executor) => countChunks(executor, corpus, tenantId));
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The saas search schema (04 §3) — `search_docs` + `search_chunks` on Postgres.
|
|
3
|
+
*
|
|
4
|
+
* `domains/search.ts` refuses to compile its SQLite chain for Postgres
|
|
5
|
+
* (`PG_DDL_UNSUPPORTED`) and says why: `search_fts` is an FTS5 external-content
|
|
6
|
+
* virtual table maintained by SQLite triggers, which is not a dialect
|
|
7
|
+
* difference — it is a different search engine. So the saas keyword tier is
|
|
8
|
+
* authored here, natively, and the two providers share only the vector tier
|
|
9
|
+
* (`cwip`'s `searchVectorTier`, one implementation for both).
|
|
10
|
+
*
|
|
11
|
+
* ## Why a GENERATED column rather than a trigger
|
|
12
|
+
*
|
|
13
|
+
* The SQLite side keeps its FTS shadow in step with three triggers, and its own
|
|
14
|
+
* header explains the hazard: "application-maintained shadow tables drift the
|
|
15
|
+
* moment one code path forgets". Postgres offers something stronger than a
|
|
16
|
+
* trigger for this — a `GENERATED ALWAYS AS (…) STORED` column cannot drift at
|
|
17
|
+
* all, because there is no code path that writes it. A trigger can be dropped, a
|
|
18
|
+
* migration can forget to recreate it, and `INSERT ... ON CONFLICT DO UPDATE`
|
|
19
|
+
* can bypass one that was written for the wrong event. A generated column has
|
|
20
|
+
* none of those failure modes: the tsvector is a function of the row, enforced
|
|
21
|
+
* by the storage engine.
|
|
22
|
+
*
|
|
23
|
+
* ## `simple` rather than `english`
|
|
24
|
+
*
|
|
25
|
+
* The text config is `simple` — no stemming, no stopwords. Two reasons, and the
|
|
26
|
+
* second is the load-bearing one:
|
|
27
|
+
*
|
|
28
|
+
* 1. This platform's corpora are multi-lingual by construction (a customer's
|
|
29
|
+
* notes, filenames, contact names). Stemming with the WRONG language is
|
|
30
|
+
* worse than not stemming: `english` drops "die"/"das" as if they were
|
|
31
|
+
* stopwords and stems "arbeiten" to nothing useful.
|
|
32
|
+
* 2. `english` discards stopwords, so a search for a title that IS a stopword
|
|
33
|
+
* phrase ("The Who", "It") returns nothing at all — and the SQLite side's
|
|
34
|
+
* `unicode61` tokenizer has no stopword list either. Two tiers that
|
|
35
|
+
* disagree about whether a document is findable is a bug report a customer
|
|
36
|
+
* files as "search works on my laptop".
|
|
37
|
+
*
|
|
38
|
+
* ## RLS
|
|
39
|
+
*
|
|
40
|
+
* `search_docs` is tenant-rooted (`SEARCH_TENANCY`), so it carries the same
|
|
41
|
+
* `tenant_isolation` policy every domain table does, and `search_chunks`
|
|
42
|
+
* inherits through its `doc_id` FK. That is defense in depth, not the primary
|
|
43
|
+
* control: the provider binds `tenant_id` into every statement it issues, the
|
|
44
|
+
* way the SQLite provider does. The policy is what makes a future statement that
|
|
45
|
+
* forgets return zero rows instead of everyone's.
|
|
46
|
+
*
|
|
47
|
+
* DDL is NEVER issued on a process start — {@link ensureSaasSearchSchema} is a
|
|
48
|
+
* named call a deployment makes once, for the same reason
|
|
49
|
+
* `ensureSaasControlPlaneSchema` is: a shared database has one schema owner, and
|
|
50
|
+
* a library that migrated on boot would be N of them racing.
|
|
51
|
+
*/
|
|
52
|
+
import type { SaasPool, StatementExecutor } from 'cwip/storage';
|
|
53
|
+
import { assertRowLevelSecurity, createPostgresControlExecutor, rlsStatements } from 'cwip/storage';
|
|
54
|
+
|
|
55
|
+
/** The tsvector expression. Title weighted `A`, body `B` — `ts_rank` reads the weights. */
|
|
56
|
+
const TSV_EXPRESSION = `setweight(to_tsvector('simple', coalesce(title, '')), 'A') || setweight(to_tsvector('simple', coalesce(body, '')), 'B')`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Weights fed to `ts_rank`, in Postgres' `{D, C, B, A}` order. Title 4× body,
|
|
60
|
+
* matching the SQLite provider's `bm25(search_fts, 4.0, 1.0)` exactly — two
|
|
61
|
+
* profiles that ranked the same corpus differently would make "it ranks wrong in
|
|
62
|
+
* production" unreproducible on a laptop.
|
|
63
|
+
*/
|
|
64
|
+
export const PG_RANK_WEIGHTS = '{0.1, 0.1, 0.25, 1.0}';
|
|
65
|
+
|
|
66
|
+
export const PG_SEARCH_DOCS_DDL = `CREATE TABLE IF NOT EXISTS search_docs (
|
|
67
|
+
id BIGSERIAL PRIMARY KEY,
|
|
68
|
+
corpus TEXT NOT NULL,
|
|
69
|
+
source_id TEXT NOT NULL,
|
|
70
|
+
tenant_id TEXT NOT NULL,
|
|
71
|
+
user_id TEXT,
|
|
72
|
+
title TEXT NOT NULL DEFAULT '',
|
|
73
|
+
body TEXT NOT NULL DEFAULT '',
|
|
74
|
+
rev INTEGER NOT NULL DEFAULT 0,
|
|
75
|
+
indexed_at TEXT NOT NULL,
|
|
76
|
+
tsv tsvector GENERATED ALWAYS AS (${TSV_EXPRESSION}) STORED
|
|
77
|
+
)`;
|
|
78
|
+
|
|
79
|
+
export const PG_SEARCH_CHUNKS_DDL = `CREATE TABLE IF NOT EXISTS search_chunks (
|
|
80
|
+
id BIGSERIAL PRIMARY KEY,
|
|
81
|
+
doc_id BIGINT NOT NULL REFERENCES search_docs(id) ON DELETE CASCADE,
|
|
82
|
+
seq INTEGER NOT NULL,
|
|
83
|
+
text TEXT NOT NULL,
|
|
84
|
+
embedding BYTEA,
|
|
85
|
+
dims INTEGER,
|
|
86
|
+
model TEXT,
|
|
87
|
+
indexed_at TEXT NOT NULL
|
|
88
|
+
)`;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Every statement that builds the saas search schema, in dependency order.
|
|
92
|
+
*
|
|
93
|
+
* The unique index on `(corpus, source_id, tenant_id)` is what makes the
|
|
94
|
+
* provider's upsert idempotent — it is the same natural key the SQLite schema
|
|
95
|
+
* declares, and 05 §A1's cross-tenant-uniqueness lesson is why `tenant_id` is
|
|
96
|
+
* in it rather than assumed.
|
|
97
|
+
*/
|
|
98
|
+
export const PG_SEARCH_DDL: readonly string[] = [
|
|
99
|
+
PG_SEARCH_DOCS_DDL,
|
|
100
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS search_docs_source ON search_docs(corpus, source_id, tenant_id)',
|
|
101
|
+
'CREATE INDEX IF NOT EXISTS search_docs_tenant ON search_docs(tenant_id, corpus, indexed_at DESC)',
|
|
102
|
+
'CREATE INDEX IF NOT EXISTS search_docs_tsv ON search_docs USING GIN(tsv)',
|
|
103
|
+
PG_SEARCH_CHUNKS_DDL,
|
|
104
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS search_chunks_seq ON search_chunks(doc_id, seq)',
|
|
105
|
+
// The vector tier's candidate scan filters on the join plus (dims, model);
|
|
106
|
+
// without this it is a sequential scan of every tenant's chunks per query.
|
|
107
|
+
'CREATE INDEX IF NOT EXISTS search_chunks_space ON search_chunks(doc_id, dims, model)',
|
|
108
|
+
// The platform's OWN policy text, not a hand-written copy. `rlsStatements`
|
|
109
|
+
// also emits `FORCE ROW LEVEL SECURITY`, which is the half a hand-written
|
|
110
|
+
// chain forgets and the half that matters here: a table's OWNER bypasses RLS
|
|
111
|
+
// unless it is FORCEd, and in this platform the application role owns its own
|
|
112
|
+
// schema. Without it the policy is decoration for exactly the connection that
|
|
113
|
+
// serves every request.
|
|
114
|
+
...rlsStatements('search_docs'),
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
// Self-check at module load, the same one `toPostgresChain` performs on a
|
|
118
|
+
// compiled chain. This chain is hand-authored (SQLite's FTS5 shadow has no
|
|
119
|
+
// Postgres translation), so it does NOT go through the compiler that would
|
|
120
|
+
// normally apply this gate — and a hand-authored chain is precisely the one that
|
|
121
|
+
// can ship a tenant-rooted table with no policy.
|
|
122
|
+
assertRowLevelSecurity(PG_SEARCH_DDL);
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Create the saas search schema. Idempotent, and called EXPLICITLY by a
|
|
126
|
+
* deployment — never on a process start. See the file header.
|
|
127
|
+
*
|
|
128
|
+
* `CREATE POLICY` has no `IF NOT EXISTS` in Postgres, so the policy statements
|
|
129
|
+
* are skipped when the policy is already there rather than being wrapped in a
|
|
130
|
+
* swallow-everything `DO` block: a block that ignores the duplicate-object error
|
|
131
|
+
* also ignores a typo in the predicate, which would leave the table enabled,
|
|
132
|
+
* forced, and isolated by a policy that never matches.
|
|
133
|
+
*/
|
|
134
|
+
export const ensureSaasSearchSchema = async (pool: SaasPool): Promise<void> => {
|
|
135
|
+
await pool.withConnection(async (connection) => {
|
|
136
|
+
const executor: StatementExecutor = createPostgresControlExecutor(connection);
|
|
137
|
+
const existing = await executor.all<{ policyname: string }>({
|
|
138
|
+
sql: "SELECT policyname FROM pg_policies WHERE tablename = 'search_docs'",
|
|
139
|
+
});
|
|
140
|
+
const present = new Set(existing.map((row) => row.policyname));
|
|
141
|
+
for (const statement of PG_SEARCH_DDL) {
|
|
142
|
+
const policy = /^CREATE\s+POLICY\s+([a-z_]+)\s+ON\b/i.exec(statement.trim());
|
|
143
|
+
if (policy?.[1] !== undefined && present.has(policy[1])) continue;
|
|
144
|
+
await executor.write({ sql: statement });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cursedbelt-server/sharing` — the fleet's ONE per-record sharing/access model.
|
|
3
|
+
*
|
|
4
|
+
* The vocabulary is `cursedbelt-core/sharing` (pure), the panel is
|
|
5
|
+
* `cursedbelt/react/sharing`, and this is the storage + resolver half. Read
|
|
6
|
+
* `./store.ts`'s header for the five properties it guarantees and `./sql.ts` for
|
|
7
|
+
* why the row filter is a SQL fragment rather than a list of ids.
|
|
8
|
+
*/
|
|
9
|
+
export { SHARE_MIGRATIONS } from "./migrations";
|
|
10
|
+
export {
|
|
11
|
+
SHARE_EVENTS_TABLE,
|
|
12
|
+
SHARE_GRANTS_TABLE,
|
|
13
|
+
SHARE_GROUPS_TABLE,
|
|
14
|
+
SHARE_GROUP_MEMBERS_TABLE,
|
|
15
|
+
} from "./migrations";
|
|
16
|
+
export {
|
|
17
|
+
GROUP_SUBJECT_PREFIX,
|
|
18
|
+
mountShareRoutes,
|
|
19
|
+
parseSubjectParam,
|
|
20
|
+
subjectParam,
|
|
21
|
+
} from "./router";
|
|
22
|
+
export type { ShareRouterOptions, ShareRouterViewer, ShareRoutesPayload } from "./router";
|
|
23
|
+
export { sharedResourceSql, sqlColumn, sqlLiteral } from "./sql";
|
|
24
|
+
export type { SharedResourceSqlOptions } from "./sql";
|
|
25
|
+
export { createShareStore, group, user } from "./store";
|
|
26
|
+
export type {
|
|
27
|
+
GrantInput,
|
|
28
|
+
RevokeInput,
|
|
29
|
+
ShareEventQuery,
|
|
30
|
+
ShareStore,
|
|
31
|
+
ShareStoreOptions,
|
|
32
|
+
ShareTarget,
|
|
33
|
+
} from "./store";
|
|
34
|
+
// Re-exported so a server consumer needs ONE import for the whole contract; the
|
|
35
|
+
// pure entry (`cursedbelt-core/sharing`) stays available for a browser or a test.
|
|
36
|
+
export {
|
|
37
|
+
accessMeets,
|
|
38
|
+
describeAccess,
|
|
39
|
+
describeShareEvent,
|
|
40
|
+
isShareAccess,
|
|
41
|
+
isShareLevel,
|
|
42
|
+
isShareSubjectType,
|
|
43
|
+
levelsMeeting,
|
|
44
|
+
normalizeSubject,
|
|
45
|
+
normalizeSubjectId,
|
|
46
|
+
resolveAccess,
|
|
47
|
+
resourceKey,
|
|
48
|
+
sameResource,
|
|
49
|
+
sameSubject,
|
|
50
|
+
SHARE_ACTIONS,
|
|
51
|
+
strongerAccess,
|
|
52
|
+
} from "cursedbelt-core/sharing/model";
|
|
53
|
+
export type {
|
|
54
|
+
ShareAccess,
|
|
55
|
+
ShareAction,
|
|
56
|
+
ShareEvent,
|
|
57
|
+
ShareGrant,
|
|
58
|
+
ShareGroup,
|
|
59
|
+
ShareGroupMember,
|
|
60
|
+
ShareLevel,
|
|
61
|
+
ShareResource,
|
|
62
|
+
ShareSubject,
|
|
63
|
+
ShareSubjectType,
|
|
64
|
+
ShareViewer,
|
|
65
|
+
} from "cursedbelt-core/sharing/model";
|