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,613 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two shapes a governed resource can take — a sqlite table and a directory
|
|
3
|
+
* of files — behind one interface the sweeper drives.
|
|
4
|
+
*
|
|
5
|
+
* Everything policy-shaped (how old, how many, what the floor is) is decided in
|
|
6
|
+
* `policy.ts` and arrives here already resolved. A target's only job is to know
|
|
7
|
+
* how to count, preview and delete ITS medium. That split is what stops the next
|
|
8
|
+
* app from needing a fourth hand-rolled sweeper.
|
|
9
|
+
*
|
|
10
|
+
* ── `preview` is not a nicety ───────────────────────────────────────────────
|
|
11
|
+
* The station UI shows "this change would remove N items now" BEFORE the owner
|
|
12
|
+
* saves it. That number has to come from the same predicate the sweep runs, or
|
|
13
|
+
* it is a guess wearing a number's clothes — so `preview` and `sweep` share
|
|
14
|
+
* their WHERE clause by construction here, rather than by two functions that
|
|
15
|
+
* were correct on the day they were written.
|
|
16
|
+
*/
|
|
17
|
+
import type { Database } from "bun:sqlite";
|
|
18
|
+
import { readdirSync, rmSync, statSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import type { ArchiveSink } from "./archive";
|
|
21
|
+
import { DEFAULT_PENDING_ARCHIVE_BUDGET_BYTES } from "./archive";
|
|
22
|
+
import type { EffectiveRetentionPolicy, RetentionDeclaration } from "./policy";
|
|
23
|
+
import { categoryArchivesBeforeDelete, cutoffFor, retentionKey } from "./policy";
|
|
24
|
+
import type { RetentionMeasurement, RetentionSweepReport } from "./store";
|
|
25
|
+
|
|
26
|
+
export interface RetentionPreview {
|
|
27
|
+
/** Items the age rule would remove. */
|
|
28
|
+
aged: number;
|
|
29
|
+
/** Items the item-count backstop would remove on top of that. */
|
|
30
|
+
overflow: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface RetentionTarget {
|
|
34
|
+
readonly declaration: RetentionDeclaration;
|
|
35
|
+
measure(): RetentionMeasurement;
|
|
36
|
+
preview(policy: EffectiveRetentionPolicy, now: number): RetentionPreview;
|
|
37
|
+
sweep(policy: EffectiveRetentionPolicy, now: number): RetentionSweepReport;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Table and column names cannot be bound as parameters, so they are
|
|
42
|
+
* interpolated — and therefore must be proven safe rather than trusted. Every
|
|
43
|
+
* caller is first-party today; this is what keeps that true after someone wires
|
|
44
|
+
* a name in from a config file.
|
|
45
|
+
*/
|
|
46
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
47
|
+
function ident(kind: string, name: string): string {
|
|
48
|
+
if (!IDENTIFIER.test(name))
|
|
49
|
+
throw new Error(`retention: unsafe ${kind} name ${JSON.stringify(name)}`);
|
|
50
|
+
return `"${name}"`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 🔴 The structural half of the `archived-history` promise.
|
|
55
|
+
*
|
|
56
|
+
* The category says the rows are written somewhere safe before they go. These
|
|
57
|
+
* two targets DELETE — that is all they can do — so a declaration carrying an
|
|
58
|
+
* archiving category must not be constructible on one. Without this refusal the
|
|
59
|
+
* category would degrade, silently and at the worst possible moment, into
|
|
60
|
+
* "delete after a LONGER window than the standard allows", which is strictly
|
|
61
|
+
* worse than having no category at all.
|
|
62
|
+
*
|
|
63
|
+
* It throws at CONSTRUCTION, where the stack names the app and the resource,
|
|
64
|
+
* rather than at the first sweep hours later in a daemon nobody is watching.
|
|
65
|
+
*/
|
|
66
|
+
function refuseArchivingCategory(declaration: RetentionDeclaration, target: string): void {
|
|
67
|
+
if (!categoryArchivesBeforeDelete(declaration.category)) return;
|
|
68
|
+
throw new Error(
|
|
69
|
+
`retention: "${retentionKey(declaration.app, declaration.id)}" declares the ` +
|
|
70
|
+
`"${declaration.category}" category, which promises the rows are archived before they are ` +
|
|
71
|
+
`deleted — and ${target} only deletes. Use createArchivingSqliteTableTarget with an ` +
|
|
72
|
+
"ArchiveSink, or pick a category that honestly describes throwing the data away.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface SqliteTableTargetOptions {
|
|
77
|
+
db: Database;
|
|
78
|
+
declaration: RetentionDeclaration;
|
|
79
|
+
table: string;
|
|
80
|
+
/** Column holding the row's age. Rows with NULL here are treated as ageless and kept. */
|
|
81
|
+
timeColumn: string;
|
|
82
|
+
/**
|
|
83
|
+
* How `timeColumn` stores time. `epoch-ms` is the default; `iso` covers the
|
|
84
|
+
* `2026-08-01T22:43:05.317Z` TEXT columns that are just as common across this
|
|
85
|
+
* fleet (`orch`'s `job_runs`, and anything written by a scheduler that reasoned
|
|
86
|
+
* in `Date().toISOString()`).
|
|
87
|
+
*
|
|
88
|
+
* ISO-8601 UTC strings sort lexicographically in the same order as the instants
|
|
89
|
+
* they name, so the comparison stays a plain `<` against a bound parameter — no
|
|
90
|
+
* per-row `strftime`, and the column's index is still usable.
|
|
91
|
+
*/
|
|
92
|
+
timeFormat?: "epoch-ms" | "iso";
|
|
93
|
+
/** Primary key used to address rows. Must be NOT NULL — `NOT IN` with a NULL yields NULL. */
|
|
94
|
+
idColumn?: string;
|
|
95
|
+
/**
|
|
96
|
+
* A SQL predicate that, when TRUE, exempts a row from the AGE sweep — the
|
|
97
|
+
* "still working state" rule. `apps/orch` uses it so a task parked for months
|
|
98
|
+
* keeps the whole trail that explains why it parked; an age-only sweep would
|
|
99
|
+
* delete exactly the rows the owner comes back to read.
|
|
100
|
+
*
|
|
101
|
+
* The item-count backstop deliberately IGNORES it: if the protected set alone
|
|
102
|
+
* can overflow the table, the bound has to win or it is not a bound. When that
|
|
103
|
+
* happens the sweeper says so loudly.
|
|
104
|
+
*/
|
|
105
|
+
protectWhere?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function createSqliteTableTarget(options: SqliteTableTargetOptions): RetentionTarget {
|
|
109
|
+
const { db, declaration } = options;
|
|
110
|
+
refuseArchivingCategory(declaration, "createSqliteTableTarget");
|
|
111
|
+
const table = ident("table", options.table);
|
|
112
|
+
const time = ident("column", options.timeColumn);
|
|
113
|
+
const id = ident("column", options.idColumn ?? "id");
|
|
114
|
+
const protect = options.protectWhere ? `(${options.protectWhere})` : null;
|
|
115
|
+
|
|
116
|
+
// The floor — "never delete the newest N, at any age" — is expressed once, as
|
|
117
|
+
// an exclusion of the newest N rows of the WHOLE table (not of the deletable
|
|
118
|
+
// subset): a floor that only counted deletable rows would let a table of
|
|
119
|
+
// entirely-protected rows plus one old row delete that row while claiming to
|
|
120
|
+
// keep three.
|
|
121
|
+
const newestN = `${id} NOT IN (SELECT ${id} FROM ${table} ORDER BY ${time} DESC LIMIT $floor)`;
|
|
122
|
+
const ageWhere =
|
|
123
|
+
`${time} IS NOT NULL AND ${time} < $cutoff` +
|
|
124
|
+
(protect ? ` AND NOT ${protect}` : "") +
|
|
125
|
+
` AND ${newestN}`;
|
|
126
|
+
|
|
127
|
+
// Statements are prepared LAZILY, and a missing table is a resource that does
|
|
128
|
+
// not exist YET rather than an error.
|
|
129
|
+
//
|
|
130
|
+
// This is not defensive padding: `orch` declares `plainjob_jobs` and
|
|
131
|
+
// `job_runs`, which its job library and scheduler create on first use — so at
|
|
132
|
+
// the moment the composition root builds its sweeper, neither table exists.
|
|
133
|
+
// Preparing eagerly threw `no such table` out of `createRetentionSweeper` and
|
|
134
|
+
// would have taken the whole daemon down at boot on a fresh machine, to bound
|
|
135
|
+
// a table with nothing in it. Any app whose schema is created by a dependency
|
|
136
|
+
// hits the same thing.
|
|
137
|
+
const tableExists = db.query(
|
|
138
|
+
"SELECT 1 AS ok FROM sqlite_master WHERE type = 'table' AND name = $name",
|
|
139
|
+
);
|
|
140
|
+
const present = (): boolean => tableExists.get({ $name: options.table } as never) !== null;
|
|
141
|
+
|
|
142
|
+
const statements = new Map<string, ReturnType<Database["query"]>>();
|
|
143
|
+
const prepared = (sql: string): ReturnType<Database["query"]> => {
|
|
144
|
+
let statement = statements.get(sql);
|
|
145
|
+
if (!statement) {
|
|
146
|
+
statement = db.query(sql);
|
|
147
|
+
statements.set(sql, statement);
|
|
148
|
+
}
|
|
149
|
+
return statement;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const countAll = () => prepared(`SELECT COUNT(*) AS n FROM ${table}`);
|
|
153
|
+
const oldest = () => prepared(`SELECT MIN(${time}) AS t FROM ${table}`);
|
|
154
|
+
const countAged = () => prepared(`SELECT COUNT(*) AS n FROM ${table} WHERE ${ageWhere}`);
|
|
155
|
+
const deleteAged = () =>
|
|
156
|
+
prepared(`DELETE FROM ${table} WHERE ${id} IN (SELECT ${id} FROM ${table} WHERE ${ageWhere})`);
|
|
157
|
+
const deleteOldest = () =>
|
|
158
|
+
prepared(
|
|
159
|
+
`DELETE FROM ${table} WHERE ${id} IN (SELECT ${id} FROM ${table} ORDER BY ${time} ASC LIMIT $excess)`,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const count = (): number => (countAll().get() as { n: number }).n;
|
|
163
|
+
|
|
164
|
+
const isIso = options.timeFormat === "iso";
|
|
165
|
+
/** The cutoff in the column's own units, so the comparison stays a plain `<`. */
|
|
166
|
+
const asColumnTime = (ms: number): number | string => (isIso ? new Date(ms).toISOString() : ms);
|
|
167
|
+
/** …and back, for the UI's "oldest item" readout. */
|
|
168
|
+
const asMs = (value: unknown): number | undefined => {
|
|
169
|
+
if (typeof value === "number") return value;
|
|
170
|
+
if (typeof value === "string") {
|
|
171
|
+
const parsed = Date.parse(value);
|
|
172
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/** How many the backstop removes once the age rule has run. */
|
|
178
|
+
function overflowExcess(policy: EffectiveRetentionPolicy, remaining: number): number {
|
|
179
|
+
if (!policy.enabled || policy.maxItems <= 0) return 0;
|
|
180
|
+
const overCap = remaining - policy.maxItems;
|
|
181
|
+
// Never below the floor, even to honor the cap: the floor is the one rule
|
|
182
|
+
// with no exceptions.
|
|
183
|
+
const abovefloor = remaining - policy.floorItems;
|
|
184
|
+
return Math.max(0, Math.min(overCap, abovefloor));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
declaration,
|
|
189
|
+
measure() {
|
|
190
|
+
if (!present()) return { items: 0 };
|
|
191
|
+
const oldestAt = asMs((oldest().get() as { t: unknown }).t);
|
|
192
|
+
return { items: count(), ...(oldestAt === undefined ? {} : { oldestAt }) };
|
|
193
|
+
},
|
|
194
|
+
preview(policy, now) {
|
|
195
|
+
if (!policy.enabled || !present()) return { aged: 0, overflow: 0 };
|
|
196
|
+
const cutoff = cutoffFor(policy, now);
|
|
197
|
+
const aged =
|
|
198
|
+
cutoff === null
|
|
199
|
+
? 0
|
|
200
|
+
: (
|
|
201
|
+
countAged().get({
|
|
202
|
+
$cutoff: asColumnTime(cutoff),
|
|
203
|
+
$floor: policy.floorItems,
|
|
204
|
+
} as never) as { n: number }
|
|
205
|
+
).n;
|
|
206
|
+
return { aged, overflow: overflowExcess(policy, count() - aged) };
|
|
207
|
+
},
|
|
208
|
+
sweep(policy, now) {
|
|
209
|
+
if (!policy.enabled || !present()) return { at: now, aged: 0, overflow: 0 };
|
|
210
|
+
let aged = 0;
|
|
211
|
+
const cutoff = cutoffFor(policy, now);
|
|
212
|
+
if (cutoff !== null) {
|
|
213
|
+
aged = deleteAged().run({
|
|
214
|
+
$cutoff: asColumnTime(cutoff),
|
|
215
|
+
$floor: policy.floorItems,
|
|
216
|
+
} as never).changes;
|
|
217
|
+
}
|
|
218
|
+
const excess = overflowExcess(policy, count());
|
|
219
|
+
const overflow = excess > 0 ? deleteOldest().run({ $excess: excess } as never).changes : 0;
|
|
220
|
+
return { at: now, aged, overflow };
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface FileDirTargetOptions {
|
|
226
|
+
declaration: RetentionDeclaration;
|
|
227
|
+
dir: string;
|
|
228
|
+
/** Only files matching this are governed. Anything else in the dir is invisible. */
|
|
229
|
+
match?: RegExp;
|
|
230
|
+
/** Injectable for tests; defaults to the file's mtime. */
|
|
231
|
+
timeOf?: (path: string, stat: { mtimeMs: number }) => number;
|
|
232
|
+
/**
|
|
233
|
+
* Files this sweep must never delete, whatever the policy says — the
|
|
234
|
+
* filesystem counterpart of `protectWhere`.
|
|
235
|
+
*
|
|
236
|
+
* Called once per sweep and given every governed file, so the caller can
|
|
237
|
+
* resolve the protected set with ONE query rather than one per file (`orch`
|
|
238
|
+
* asks its database for the attempts that are still live or whose task has not
|
|
239
|
+
* settled). Unlike `protectWhere`, this holds against the byte and item caps
|
|
240
|
+
* TOO: a bound that deletes a running worker's live transcript to save disk has
|
|
241
|
+
* traded a disk problem for a debugging one, and unlinking a file that is still
|
|
242
|
+
* being appended to loses the writer's output with no error anywhere.
|
|
243
|
+
*/
|
|
244
|
+
protect?: (
|
|
245
|
+
files: ReadonlyArray<{ name: string; path: string; at: number; bytes: number }>,
|
|
246
|
+
) => Set<string>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
interface FileEntry {
|
|
250
|
+
name: string;
|
|
251
|
+
path: string;
|
|
252
|
+
at: number;
|
|
253
|
+
bytes: number;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* A directory of files — attempt transcripts, generated art, exports.
|
|
258
|
+
*
|
|
259
|
+
* Only files DIRECTLY in `dir` and matching `match` are ever considered: a tool
|
|
260
|
+
* that deletes things gets to delete only what it can prove it owns, which is
|
|
261
|
+
* the rule `deploy-engine/backupRetention.ts` arrived at after the same
|
|
262
|
+
* question. Subdirectories are left entirely alone.
|
|
263
|
+
*/
|
|
264
|
+
export function createFileDirTarget(options: FileDirTargetOptions): RetentionTarget {
|
|
265
|
+
const { declaration, dir } = options;
|
|
266
|
+
refuseArchivingCategory(declaration, "createFileDirTarget");
|
|
267
|
+
const match = options.match;
|
|
268
|
+
const timeOf = options.timeOf ?? ((_p, stat) => stat.mtimeMs);
|
|
269
|
+
|
|
270
|
+
function list(): FileEntry[] {
|
|
271
|
+
let names: string[];
|
|
272
|
+
try {
|
|
273
|
+
names = readdirSync(dir);
|
|
274
|
+
} catch {
|
|
275
|
+
return []; // not created yet — a resource with nothing in it
|
|
276
|
+
}
|
|
277
|
+
const entries: FileEntry[] = [];
|
|
278
|
+
for (const name of names) {
|
|
279
|
+
if (match && !match.test(name)) continue;
|
|
280
|
+
const path = join(dir, name);
|
|
281
|
+
try {
|
|
282
|
+
const stat = statSync(path);
|
|
283
|
+
if (!stat.isFile()) continue;
|
|
284
|
+
entries.push({ name, path, at: timeOf(path, stat), bytes: stat.size });
|
|
285
|
+
} catch {
|
|
286
|
+
// vanished between readdir and stat — nothing to govern
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return entries.sort((a, b) => b.at - a.at); // newest first
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** The plan, shared by preview and sweep so they cannot disagree. */
|
|
293
|
+
function plan(
|
|
294
|
+
policy: EffectiveRetentionPolicy,
|
|
295
|
+
now: number,
|
|
296
|
+
): { aged: FileEntry[]; overflow: FileEntry[]; protectedCount: number } {
|
|
297
|
+
const entries = list();
|
|
298
|
+
if (!policy.enabled) return { aged: [], overflow: [], protectedCount: 0 };
|
|
299
|
+
|
|
300
|
+
// Protected by the caller (live writer / unsettled task), then by the floor.
|
|
301
|
+
// Both are absolute: neither the age rule nor either cap may touch them.
|
|
302
|
+
const kept = new Set(options.protect ? options.protect(entries) : []);
|
|
303
|
+
for (const entry of entries.slice(0, policy.floorItems)) kept.add(entry.path);
|
|
304
|
+
const deletable = entries.filter((e) => !kept.has(e.path));
|
|
305
|
+
|
|
306
|
+
const cutoff = cutoffFor(policy, now);
|
|
307
|
+
const aged = cutoff === null ? [] : deletable.filter((e) => e.at < cutoff);
|
|
308
|
+
const agedPaths = new Set(aged.map((e) => e.path));
|
|
309
|
+
|
|
310
|
+
// Oldest first: the cap always gives up the least recently useful thing.
|
|
311
|
+
const candidates = deletable.filter((e) => !agedPaths.has(e.path)).sort((a, b) => a.at - b.at);
|
|
312
|
+
const overflow: FileEntry[] = [];
|
|
313
|
+
|
|
314
|
+
if (policy.maxItems > 0) {
|
|
315
|
+
let remaining = entries.length - aged.length;
|
|
316
|
+
for (const entry of candidates) {
|
|
317
|
+
if (remaining <= policy.maxItems) break;
|
|
318
|
+
overflow.push(entry);
|
|
319
|
+
remaining--;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (policy.maxBytes > 0) {
|
|
323
|
+
const chosen = new Set(overflow.map((e) => e.path));
|
|
324
|
+
let bytes = entries.reduce(
|
|
325
|
+
(sum, e) => (agedPaths.has(e.path) || chosen.has(e.path) ? sum : sum + e.bytes),
|
|
326
|
+
0,
|
|
327
|
+
);
|
|
328
|
+
for (const entry of candidates) {
|
|
329
|
+
if (bytes <= policy.maxBytes) break;
|
|
330
|
+
if (chosen.has(entry.path)) continue;
|
|
331
|
+
overflow.push(entry);
|
|
332
|
+
chosen.add(entry.path);
|
|
333
|
+
bytes -= entry.bytes;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { aged, overflow, protectedCount: entries.length - deletable.length };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
declaration,
|
|
341
|
+
measure() {
|
|
342
|
+
const entries = list();
|
|
343
|
+
const result: RetentionMeasurement = {
|
|
344
|
+
items: entries.length,
|
|
345
|
+
bytes: entries.reduce((sum, e) => sum + e.bytes, 0),
|
|
346
|
+
};
|
|
347
|
+
const last = entries[entries.length - 1];
|
|
348
|
+
if (last) result.oldestAt = last.at;
|
|
349
|
+
return result;
|
|
350
|
+
},
|
|
351
|
+
preview(policy, now) {
|
|
352
|
+
const { aged, overflow } = plan(policy, now);
|
|
353
|
+
return { aged: aged.length, overflow: overflow.length };
|
|
354
|
+
},
|
|
355
|
+
sweep(policy, now) {
|
|
356
|
+
const { aged, overflow, protectedCount } = plan(policy, now);
|
|
357
|
+
let bytesFreed = 0;
|
|
358
|
+
let agedRemoved = 0;
|
|
359
|
+
let overflowRemoved = 0;
|
|
360
|
+
for (const [entries, isAged] of [
|
|
361
|
+
[aged, true],
|
|
362
|
+
[overflow, false],
|
|
363
|
+
] as const) {
|
|
364
|
+
for (const entry of entries) {
|
|
365
|
+
try {
|
|
366
|
+
rmSync(entry.path, { force: true });
|
|
367
|
+
bytesFreed += entry.bytes;
|
|
368
|
+
if (isAged) agedRemoved++;
|
|
369
|
+
else overflowRemoved++;
|
|
370
|
+
} catch {
|
|
371
|
+
// A file that will not delete is a housekeeping problem, never a
|
|
372
|
+
// reason to abandon the rest of the sweep.
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
at: now,
|
|
378
|
+
aged: agedRemoved,
|
|
379
|
+
overflow: overflowRemoved,
|
|
380
|
+
bytesFreed,
|
|
381
|
+
protectedItems: protectedCount,
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ── archive-then-trim ────────────────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
export interface ArchivingSqliteTableTargetOptions
|
|
390
|
+
extends Omit<SqliteTableTargetOptions, "declaration"> {
|
|
391
|
+
declaration: RetentionDeclaration;
|
|
392
|
+
/** Where the rows go before they leave the table. */
|
|
393
|
+
archive: ArchiveSink;
|
|
394
|
+
/**
|
|
395
|
+
* Columns written to the archive, in order. Defaults to every column the
|
|
396
|
+
* table has at construction — which is what an append-only history wants: the
|
|
397
|
+
* archive is the row, not a projection somebody chose on a Tuesday.
|
|
398
|
+
*/
|
|
399
|
+
columns?: string[];
|
|
400
|
+
/**
|
|
401
|
+
* Most rows one sweep will move. The sweep is synchronous and the rows are
|
|
402
|
+
* held in memory to be serialized, so a first run against years of history
|
|
403
|
+
* must not try to do it in one bite. Successive sweeps drain the rest.
|
|
404
|
+
*/
|
|
405
|
+
maxBatch?: number;
|
|
406
|
+
/**
|
|
407
|
+
* How many bytes of uncollected archive a STAGED home may hold before this
|
|
408
|
+
* target refuses to trim. See `archive.ts` — this is the "refuse if the last
|
|
409
|
+
* successful pull is stale" rule, expressed in the one fact a deployed box can
|
|
410
|
+
* observe for itself.
|
|
411
|
+
*/
|
|
412
|
+
pendingBudgetBytes?: number;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* A sqlite table whose aged rows are ARCHIVED and then deleted — the runtime for
|
|
417
|
+
* the `archived-history` category.
|
|
418
|
+
*
|
|
419
|
+
* Everything about WHICH rows is shared with `createSqliteTableTarget`: the same
|
|
420
|
+
* age predicate, the same `protectWhere` liveness exemption, the same floor, the
|
|
421
|
+
* same cap. What differs is the one step in the middle, and it is the whole
|
|
422
|
+
* point: nothing is deleted until `archive.write()` has proven the bytes are on
|
|
423
|
+
* disk, and `write()` throws rather than reporting failure so there is no path
|
|
424
|
+
* where "the archive did not happen" reaches a DELETE.
|
|
425
|
+
*
|
|
426
|
+
* 🔴 The item-count backstop archives too. A cap that dumped the oldest rows
|
|
427
|
+
* straight to /dev/null the moment a table overflowed would mean the ONE case
|
|
428
|
+
* where the history is growing fastest is the one case it is destroyed — an
|
|
429
|
+
* archiving resource that has an unarchived escape hatch is not an archiving
|
|
430
|
+
* resource.
|
|
431
|
+
*/
|
|
432
|
+
export function createArchivingSqliteTableTarget(
|
|
433
|
+
options: ArchivingSqliteTableTargetOptions,
|
|
434
|
+
): RetentionTarget {
|
|
435
|
+
const { db, declaration, archive } = options;
|
|
436
|
+
if (!categoryArchivesBeforeDelete(declaration.category)) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
`retention: "${retentionKey(declaration.app, declaration.id)}" uses ` +
|
|
439
|
+
"createArchivingSqliteTableTarget but its category does not archive before deleting " +
|
|
440
|
+
`(${declaration.category}). Use createSqliteTableTarget, or declare "archived-history".`,
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
const key = retentionKey(declaration.app, declaration.id);
|
|
444
|
+
const table = ident("table", options.table);
|
|
445
|
+
const time = ident("column", options.timeColumn);
|
|
446
|
+
const idColumn = options.idColumn ?? "id";
|
|
447
|
+
const id = ident("column", idColumn);
|
|
448
|
+
const protect = options.protectWhere ? `(${options.protectWhere})` : null;
|
|
449
|
+
const maxBatch = options.maxBatch ?? 50_000;
|
|
450
|
+
const pendingBudget = options.pendingBudgetBytes ?? DEFAULT_PENDING_ARCHIVE_BUDGET_BYTES;
|
|
451
|
+
|
|
452
|
+
// Identical to the plain target's, deliberately: the predicate that decides
|
|
453
|
+
// what ages out must not fork, or the preview the owner reads and the sweep
|
|
454
|
+
// that runs stop being the same question.
|
|
455
|
+
const newestN = `${id} NOT IN (SELECT ${id} FROM ${table} ORDER BY ${time} DESC LIMIT $floor)`;
|
|
456
|
+
const ageWhere =
|
|
457
|
+
`${time} IS NOT NULL AND ${time} < $cutoff` +
|
|
458
|
+
(protect ? ` AND NOT ${protect}` : "") +
|
|
459
|
+
` AND ${newestN}`;
|
|
460
|
+
|
|
461
|
+
const tableExists = db.query(
|
|
462
|
+
"SELECT 1 AS ok FROM sqlite_master WHERE type = 'table' AND name = $name",
|
|
463
|
+
);
|
|
464
|
+
const present = (): boolean => tableExists.get({ $name: options.table } as never) !== null;
|
|
465
|
+
|
|
466
|
+
const statements = new Map<string, ReturnType<Database["query"]>>();
|
|
467
|
+
const prepared = (sql: string): ReturnType<Database["query"]> => {
|
|
468
|
+
let statement = statements.get(sql);
|
|
469
|
+
if (!statement) {
|
|
470
|
+
statement = db.query(sql);
|
|
471
|
+
statements.set(sql, statement);
|
|
472
|
+
}
|
|
473
|
+
return statement;
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
/** Resolved lazily for the same reason statements are: the table may not exist yet. */
|
|
477
|
+
let columnList: string | null = null;
|
|
478
|
+
const columns = (): string => {
|
|
479
|
+
if (columnList) return columnList;
|
|
480
|
+
if (options.columns?.length) {
|
|
481
|
+
columnList = options.columns.map((c) => ident("column", c)).join(", ");
|
|
482
|
+
return columnList;
|
|
483
|
+
}
|
|
484
|
+
const info = db.query(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
|
|
485
|
+
columnList = info.map((row) => ident("column", row.name)).join(", ") || "*";
|
|
486
|
+
return columnList;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const count = (): number =>
|
|
490
|
+
(prepared(`SELECT COUNT(*) AS n FROM ${table}`).get() as { n: number }).n;
|
|
491
|
+
|
|
492
|
+
const isIso = options.timeFormat === "iso";
|
|
493
|
+
const asColumnTime = (ms: number): number | string => (isIso ? new Date(ms).toISOString() : ms);
|
|
494
|
+
const asMs = (value: unknown): number | undefined => {
|
|
495
|
+
if (typeof value === "number") return value;
|
|
496
|
+
if (typeof value === "string") {
|
|
497
|
+
const parsed = Date.parse(value);
|
|
498
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
499
|
+
}
|
|
500
|
+
return undefined;
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
function overflowExcess(policy: EffectiveRetentionPolicy, remaining: number): number {
|
|
504
|
+
if (!policy.enabled || policy.maxItems <= 0) return 0;
|
|
505
|
+
return Math.max(0, Math.min(remaining - policy.maxItems, remaining - policy.floorItems));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const agedRows = (cutoff: number, floor: number, limit: number): Array<Record<string, unknown>> =>
|
|
509
|
+
prepared(
|
|
510
|
+
`SELECT ${columns()} FROM ${table} WHERE ${ageWhere} ORDER BY ${time} ASC LIMIT $limit`,
|
|
511
|
+
).all({ $cutoff: asColumnTime(cutoff), $floor: floor, $limit: limit } as never) as Array<
|
|
512
|
+
Record<string, unknown>
|
|
513
|
+
>;
|
|
514
|
+
|
|
515
|
+
const oldestRows = (excess: number): Array<Record<string, unknown>> =>
|
|
516
|
+
prepared(
|
|
517
|
+
`SELECT ${columns()} FROM ${table} ORDER BY ${time} ASC LIMIT $excess`,
|
|
518
|
+
).all({ $excess: excess } as never) as Array<Record<string, unknown>>;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Archive the rows, THEN delete exactly those ids. Never a WHERE re-run: the
|
|
522
|
+
* rows that were archived and the rows that are deleted have to be the same
|
|
523
|
+
* set, and a predicate evaluated twice against a live table is two sets.
|
|
524
|
+
*/
|
|
525
|
+
function archiveThenDelete(rows: Array<Record<string, unknown>>, at: number): number {
|
|
526
|
+
const ids = rows.map((row) => row[idColumn]);
|
|
527
|
+
if (ids.some((value) => value === null || value === undefined)) {
|
|
528
|
+
throw new Error(
|
|
529
|
+
`retention: ${key} cannot archive rows whose "${idColumn}" is NULL — a row this sweep ` +
|
|
530
|
+
"could not address afterwards must not be archived, or the archive would grow a copy " +
|
|
531
|
+
"of a row that stays in the table for ever.",
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
const segment = archive.write(rows, at);
|
|
535
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
536
|
+
db.query(`DELETE FROM ${table} WHERE ${id} IN (${placeholders})`).run(...(ids as never[]));
|
|
537
|
+
return segment.bytes;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
declaration,
|
|
542
|
+
measure() {
|
|
543
|
+
if (!present()) return { items: 0 };
|
|
544
|
+
const oldestAt = asMs(
|
|
545
|
+
(prepared(`SELECT MIN(${time}) AS t FROM ${table}`).get() as { t: unknown }).t,
|
|
546
|
+
);
|
|
547
|
+
return { items: count(), ...(oldestAt === undefined ? {} : { oldestAt }) };
|
|
548
|
+
},
|
|
549
|
+
preview(policy, now) {
|
|
550
|
+
if (!policy.enabled || !present()) return { aged: 0, overflow: 0 };
|
|
551
|
+
const cutoff = cutoffFor(policy, now);
|
|
552
|
+
const aged =
|
|
553
|
+
cutoff === null
|
|
554
|
+
? 0
|
|
555
|
+
: (
|
|
556
|
+
prepared(`SELECT COUNT(*) AS n FROM ${table} WHERE ${ageWhere}`).get({
|
|
557
|
+
$cutoff: asColumnTime(cutoff),
|
|
558
|
+
$floor: policy.floorItems,
|
|
559
|
+
} as never) as { n: number }
|
|
560
|
+
).n;
|
|
561
|
+
return { aged: Math.min(aged, maxBatch), overflow: overflowExcess(policy, count() - aged) };
|
|
562
|
+
},
|
|
563
|
+
sweep(policy, now) {
|
|
564
|
+
if (!policy.enabled || !present()) return { at: now, aged: 0, overflow: 0 };
|
|
565
|
+
|
|
566
|
+
// 🔴 The staleness refusal, before a single row is read. A staged archive
|
|
567
|
+
// that nobody is collecting means the box would be trading a bounded table
|
|
568
|
+
// for an unbounded archive on the same disk — and, worse, doing it while
|
|
569
|
+
// the ONLY copy of the trimmed history sits on the box the trim was
|
|
570
|
+
// supposed to protect. Stop, and say why.
|
|
571
|
+
const pending = archive.pendingBytes();
|
|
572
|
+
if (!archive.terminal && pending > pendingBudget) {
|
|
573
|
+
return {
|
|
574
|
+
at: now,
|
|
575
|
+
aged: 0,
|
|
576
|
+
overflow: 0,
|
|
577
|
+
blocked:
|
|
578
|
+
`${Math.round(pending / 1048576)} MB of archived history in ${archive.dir} has not been ` +
|
|
579
|
+
`collected (budget ${Math.round(pendingBudget / 1048576)} MB). Nothing was trimmed: the ` +
|
|
580
|
+
"archive is this history's only other copy, so a stale collector must stop the trim, " +
|
|
581
|
+
"not be outrun by it. Run the Mac's `bun run backups:pull-prod`.",
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
let aged = 0;
|
|
586
|
+
let archivedBytes = 0;
|
|
587
|
+
const cutoff = cutoffFor(policy, now);
|
|
588
|
+
if (cutoff !== null) {
|
|
589
|
+
const rows = agedRows(cutoff, policy.floorItems, maxBatch);
|
|
590
|
+
if (rows.length > 0) {
|
|
591
|
+
archivedBytes += archiveThenDelete(rows, now);
|
|
592
|
+
aged = rows.length;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
let overflow = 0;
|
|
596
|
+
const excess = Math.min(overflowExcess(policy, count()), maxBatch);
|
|
597
|
+
if (excess > 0) {
|
|
598
|
+
const rows = oldestRows(excess);
|
|
599
|
+
if (rows.length > 0) {
|
|
600
|
+
archivedBytes += archiveThenDelete(rows, now + 1);
|
|
601
|
+
overflow = rows.length;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const moved = aged + overflow;
|
|
605
|
+
return {
|
|
606
|
+
at: now,
|
|
607
|
+
aged,
|
|
608
|
+
overflow,
|
|
609
|
+
...(moved > 0 ? { archived: moved, archivedBytes } : {}),
|
|
610
|
+
};
|
|
611
|
+
},
|
|
612
|
+
};
|
|
613
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The saas search tier (04 §3) — the far side of `SearchProvider` for a pooled
|
|
2
|
+
// Postgres. `createSaasHostServices` is the near side a deployment actually
|
|
3
|
+
// calls; these are exported for a host that wires the tier by hand and for the
|
|
4
|
+
// migration step that creates the schema.
|
|
5
|
+
export { createPgSearchProvider, type PgSearchProviderOptions } from './pgSearchProvider';
|
|
6
|
+
export {
|
|
7
|
+
ensureSaasSearchSchema,
|
|
8
|
+
PG_RANK_WEIGHTS,
|
|
9
|
+
PG_SEARCH_CHUNKS_DDL,
|
|
10
|
+
PG_SEARCH_DDL,
|
|
11
|
+
PG_SEARCH_DOCS_DDL,
|
|
12
|
+
} from './pgSearchSchema';
|