@sixthsummit/ship-api 0.0.0-stage → 4.1.1
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/CHANGELOG.md +605 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +58 -2
- package/cli/commands/add.js +537 -0
- package/cli/commands/contract.js +2 -0
- package/cli/commands/create.js +250 -0
- package/cli/commands/dev.js +227 -0
- package/cli/commands/doctor.js +286 -0
- package/cli/commands/generate.js +937 -0
- package/cli/commands/graph.js +2 -0
- package/cli/commands/migrate-orm.js +1171 -0
- package/cli/commands/update.js +37 -0
- package/cli/ship-api.js +285 -0
- package/lib/app/auth/AuthProvider.d.ts +91 -0
- package/lib/app/auth/AuthProvider.d.ts.map +1 -0
- package/lib/app/auth/AuthProvider.js +13 -0
- package/lib/app/auth/AuthProvider.js.map +1 -0
- package/lib/app/auth/adapters/firebase.d.ts +79 -0
- package/lib/app/auth/adapters/firebase.d.ts.map +1 -0
- package/lib/app/auth/adapters/firebase.js +122 -0
- package/lib/app/auth/adapters/firebase.js.map +1 -0
- package/lib/app/auth/adapters/jwtCookie.d.ts +55 -0
- package/lib/app/auth/adapters/jwtCookie.d.ts.map +1 -0
- package/lib/app/auth/adapters/jwtCookie.js +120 -0
- package/lib/app/auth/adapters/jwtCookie.js.map +1 -0
- package/lib/app/auth/index.d.ts +8 -0
- package/lib/app/auth/index.d.ts.map +1 -0
- package/lib/app/auth/index.js +7 -0
- package/lib/app/auth/index.js.map +1 -0
- package/lib/app/auth/jwt/AuthFlow.d.ts +94 -0
- package/lib/app/auth/jwt/AuthFlow.d.ts.map +1 -0
- package/lib/app/auth/jwt/AuthFlow.js +127 -0
- package/lib/app/auth/jwt/AuthFlow.js.map +1 -0
- package/lib/app/auth/jwt/DrizzleRefreshTokenStore.d.ts +80 -0
- package/lib/app/auth/jwt/DrizzleRefreshTokenStore.d.ts.map +1 -0
- package/lib/app/auth/jwt/DrizzleRefreshTokenStore.js +134 -0
- package/lib/app/auth/jwt/DrizzleRefreshTokenStore.js.map +1 -0
- package/lib/app/auth/jwt/JwtSigner.d.ts +123 -0
- package/lib/app/auth/jwt/JwtSigner.d.ts.map +1 -0
- package/lib/app/auth/jwt/JwtSigner.js +118 -0
- package/lib/app/auth/jwt/JwtSigner.js.map +1 -0
- package/lib/app/auth/jwt/RefreshTokenStore.d.ts +97 -0
- package/lib/app/auth/jwt/RefreshTokenStore.d.ts.map +1 -0
- package/lib/app/auth/jwt/RefreshTokenStore.js +122 -0
- package/lib/app/auth/jwt/RefreshTokenStore.js.map +1 -0
- package/lib/app/auth/permissionDefinition.d.ts +20 -0
- package/lib/app/auth/permissionDefinition.d.ts.map +1 -0
- package/lib/app/auth/permissionDefinition.js +2 -0
- package/lib/app/auth/permissionDefinition.js.map +1 -0
- package/lib/app/context/RequestContext.d.ts +59 -0
- package/lib/app/context/RequestContext.d.ts.map +1 -0
- package/lib/app/context/RequestContext.js +25 -0
- package/lib/app/context/RequestContext.js.map +1 -0
- package/lib/app/db/QueryOptions.d.ts +82 -0
- package/lib/app/db/QueryOptions.d.ts.map +1 -0
- package/lib/app/db/QueryOptions.js +37 -0
- package/lib/app/db/QueryOptions.js.map +1 -0
- package/lib/app/db/baseFields.d.ts +60 -0
- package/lib/app/db/baseFields.d.ts.map +1 -0
- package/lib/app/db/baseFields.js +54 -0
- package/lib/app/db/baseFields.js.map +1 -0
- package/lib/app/db/buildWhere.d.ts +22 -0
- package/lib/app/db/buildWhere.d.ts.map +1 -0
- package/lib/app/db/buildWhere.js +75 -0
- package/lib/app/db/buildWhere.js.map +1 -0
- package/lib/app/db/client.d.ts +111 -0
- package/lib/app/db/client.d.ts.map +1 -0
- package/lib/app/db/client.js +99 -0
- package/lib/app/db/client.js.map +1 -0
- package/lib/app/db/tenantScoped.d.ts +59 -0
- package/lib/app/db/tenantScoped.d.ts.map +1 -0
- package/lib/app/db/tenantScoped.js +54 -0
- package/lib/app/db/tenantScoped.js.map +1 -0
- package/lib/app/db/transaction.d.ts +58 -0
- package/lib/app/db/transaction.d.ts.map +1 -0
- package/lib/app/db/transaction.js +106 -0
- package/lib/app/db/transaction.js.map +1 -0
- package/lib/app/health/HealthCheck.d.ts +121 -0
- package/lib/app/health/HealthCheck.d.ts.map +1 -0
- package/lib/app/health/HealthCheck.js +151 -0
- package/lib/app/health/HealthCheck.js.map +1 -0
- package/lib/app/health/redisHealthCheck.d.ts +14 -0
- package/lib/app/health/redisHealthCheck.d.ts.map +1 -0
- package/lib/app/health/redisHealthCheck.js +21 -0
- package/lib/app/health/redisHealthCheck.js.map +1 -0
- package/lib/app/lifecycle/Bootstrapper.d.ts +92 -0
- package/lib/app/lifecycle/Bootstrapper.d.ts.map +1 -0
- package/lib/app/lifecycle/Bootstrapper.js +99 -0
- package/lib/app/lifecycle/Bootstrapper.js.map +1 -0
- package/lib/app/registry/routes.d.ts +89 -0
- package/lib/app/registry/routes.d.ts.map +1 -0
- package/lib/app/registry/routes.js +72 -0
- package/lib/app/registry/routes.js.map +1 -0
- package/lib/app/services/DrizzleEntityService.d.ts +123 -0
- package/lib/app/services/DrizzleEntityService.d.ts.map +1 -0
- package/lib/app/services/DrizzleEntityService.js +342 -0
- package/lib/app/services/DrizzleEntityService.js.map +1 -0
- package/lib/app/services/RedisProvider.d.ts +52 -0
- package/lib/app/services/RedisProvider.d.ts.map +1 -0
- package/lib/app/services/RedisProvider.js +75 -0
- package/lib/app/services/RedisProvider.js.map +1 -0
- package/lib/app/services/cache/Cache.d.ts +70 -0
- package/lib/app/services/cache/Cache.d.ts.map +1 -0
- package/lib/app/services/cache/Cache.js +18 -0
- package/lib/app/services/cache/Cache.js.map +1 -0
- package/lib/app/services/cache/InMemoryCache.d.ts +35 -0
- package/lib/app/services/cache/InMemoryCache.d.ts.map +1 -0
- package/lib/app/services/cache/InMemoryCache.js +76 -0
- package/lib/app/services/cache/InMemoryCache.js.map +1 -0
- package/lib/app/services/cache/RedisCache.d.ts +99 -0
- package/lib/app/services/cache/RedisCache.d.ts.map +1 -0
- package/lib/app/services/cache/RedisCache.js +150 -0
- package/lib/app/services/cache/RedisCache.js.map +1 -0
- package/lib/app/services/cache/cached.d.ts +30 -0
- package/lib/app/services/cache/cached.d.ts.map +1 -0
- package/lib/app/services/cache/cached.js +38 -0
- package/lib/app/services/cache/cached.js.map +1 -0
- package/lib/app/services/cache/index.d.ts +5 -0
- package/lib/app/services/cache/index.d.ts.map +1 -0
- package/lib/app/services/cache/index.js +4 -0
- package/lib/app/services/cache/index.js.map +1 -0
- package/lib/app/services/cron/defineCron.d.ts +57 -0
- package/lib/app/services/cron/defineCron.d.ts.map +1 -0
- package/lib/app/services/cron/defineCron.js +15 -0
- package/lib/app/services/cron/defineCron.js.map +1 -0
- package/lib/app/services/cron/index.d.ts +2 -0
- package/lib/app/services/cron/index.d.ts.map +1 -0
- package/lib/app/services/cron/index.js +2 -0
- package/lib/app/services/cron/index.js.map +1 -0
- package/lib/app/services/hooks/LifecycleHooks.d.ts +61 -0
- package/lib/app/services/hooks/LifecycleHooks.d.ts.map +1 -0
- package/lib/app/services/hooks/LifecycleHooks.js +85 -0
- package/lib/app/services/hooks/LifecycleHooks.js.map +1 -0
- package/lib/app/services/jobs/JobsManager.d.ts +67 -0
- package/lib/app/services/jobs/JobsManager.d.ts.map +1 -0
- package/lib/app/services/jobs/JobsManager.js +121 -0
- package/lib/app/services/jobs/JobsManager.js.map +1 -0
- package/lib/app/services/jobs/bullBoard.d.ts +72 -0
- package/lib/app/services/jobs/bullBoard.d.ts.map +1 -0
- package/lib/app/services/jobs/bullBoard.js +96 -0
- package/lib/app/services/jobs/bullBoard.js.map +1 -0
- package/lib/app/services/jobs/defineJob.d.ts +81 -0
- package/lib/app/services/jobs/defineJob.d.ts.map +1 -0
- package/lib/app/services/jobs/defineJob.js +49 -0
- package/lib/app/services/jobs/defineJob.js.map +1 -0
- package/lib/app/services/jobs/index.d.ts +4 -0
- package/lib/app/services/jobs/index.d.ts.map +1 -0
- package/lib/app/services/jobs/index.js +4 -0
- package/lib/app/services/jobs/index.js.map +1 -0
- package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.d.ts +23 -0
- package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.d.ts.map +1 -0
- package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.js +51 -0
- package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.js.map +1 -0
- package/lib/app/services/rateLimit/RateLimitBackend.d.ts +41 -0
- package/lib/app/services/rateLimit/RateLimitBackend.d.ts.map +1 -0
- package/lib/app/services/rateLimit/RateLimitBackend.js +19 -0
- package/lib/app/services/rateLimit/RateLimitBackend.js.map +1 -0
- package/lib/app/services/rateLimit/RedisSlidingWindowBackend.d.ts +16 -0
- package/lib/app/services/rateLimit/RedisSlidingWindowBackend.d.ts.map +1 -0
- package/lib/app/services/rateLimit/RedisSlidingWindowBackend.js +80 -0
- package/lib/app/services/rateLimit/RedisSlidingWindowBackend.js.map +1 -0
- package/lib/app/services/rateLimit/index.d.ts +4 -0
- package/lib/app/services/rateLimit/index.d.ts.map +1 -0
- package/lib/app/services/rateLimit/index.js +3 -0
- package/lib/app/services/rateLimit/index.js.map +1 -0
- package/lib/app/services/storage/DiskStorageAdapter.d.ts +62 -0
- package/lib/app/services/storage/DiskStorageAdapter.d.ts.map +1 -0
- package/lib/app/services/storage/DiskStorageAdapter.js +295 -0
- package/lib/app/services/storage/DiskStorageAdapter.js.map +1 -0
- package/lib/app/services/storage/GCSStorageAdapter.d.ts +44 -0
- package/lib/app/services/storage/GCSStorageAdapter.d.ts.map +1 -0
- package/lib/app/services/storage/GCSStorageAdapter.js +133 -0
- package/lib/app/services/storage/GCSStorageAdapter.js.map +1 -0
- package/lib/app/services/storage/S3StorageAdapter.d.ts +57 -0
- package/lib/app/services/storage/S3StorageAdapter.d.ts.map +1 -0
- package/lib/app/services/storage/S3StorageAdapter.js +162 -0
- package/lib/app/services/storage/S3StorageAdapter.js.map +1 -0
- package/lib/app/services/storage/StorageService.d.ts +88 -0
- package/lib/app/services/storage/StorageService.d.ts.map +1 -0
- package/lib/app/services/storage/StorageService.js +18 -0
- package/lib/app/services/storage/StorageService.js.map +1 -0
- package/lib/app/services/storage/index.d.ts +5 -0
- package/lib/app/services/storage/index.d.ts.map +1 -0
- package/lib/app/services/storage/index.js +4 -0
- package/lib/app/services/storage/index.js.map +1 -0
- package/lib/app/utils/ContextValidators.d.ts +46 -0
- package/lib/app/utils/ContextValidators.d.ts.map +1 -0
- package/lib/app/utils/ContextValidators.js +81 -0
- package/lib/app/utils/ContextValidators.js.map +1 -0
- package/lib/app/utils/defineEnv.d.ts +80 -0
- package/lib/app/utils/defineEnv.d.ts.map +1 -0
- package/lib/app/utils/defineEnv.js +95 -0
- package/lib/app/utils/defineEnv.js.map +1 -0
- package/lib/client.d.ts +139 -0
- package/lib/client.d.ts.map +1 -0
- package/lib/client.js +98 -0
- package/lib/client.js.map +1 -0
- package/lib/contract/application.d.ts +3 -0
- package/lib/contract/application.d.ts.map +1 -0
- package/lib/contract/application.js +2 -0
- package/lib/contract/application.js.map +1 -0
- package/lib/contract/client.d.ts +3 -0
- package/lib/contract/client.d.ts.map +1 -0
- package/lib/contract/client.js +3 -0
- package/lib/contract/client.js.map +1 -0
- package/lib/contract/index.d.ts +3 -0
- package/lib/contract/index.d.ts.map +1 -0
- package/lib/contract/index.js +3 -0
- package/lib/contract/index.js.map +1 -0
- package/lib/contract/manifest.d.ts +3 -0
- package/lib/contract/manifest.d.ts.map +1 -0
- package/lib/contract/manifest.js +2 -0
- package/lib/contract/manifest.js.map +1 -0
- package/lib/contract/openapi.d.ts +3 -0
- package/lib/contract/openapi.d.ts.map +1 -0
- package/lib/contract/openapi.js +3 -0
- package/lib/contract/openapi.js.map +1 -0
- package/lib/contract/project.d.ts +3 -0
- package/lib/contract/project.d.ts.map +1 -0
- package/lib/contract/project.js +3 -0
- package/lib/contract/project.js.map +1 -0
- package/lib/contract/schema.d.ts +3 -0
- package/lib/contract/schema.d.ts.map +1 -0
- package/lib/contract/schema.js +3 -0
- package/lib/contract/schema.js.map +1 -0
- package/lib/drizzle/index.d.ts +3 -0
- package/lib/drizzle/index.d.ts.map +1 -0
- package/lib/drizzle/index.js +3 -0
- package/lib/drizzle/index.js.map +1 -0
- package/lib/hono/auth.d.ts +6 -0
- package/lib/hono/auth.d.ts.map +1 -0
- package/lib/hono/auth.js +54 -0
- package/lib/hono/auth.js.map +1 -0
- package/lib/hono/index.d.ts +4 -0
- package/lib/hono/index.d.ts.map +1 -0
- package/lib/hono/index.js +4 -0
- package/lib/hono/index.js.map +1 -0
- package/lib/http/app/createApp.d.ts +132 -0
- package/lib/http/app/createApp.d.ts.map +1 -0
- package/lib/http/app/createApp.js +215 -0
- package/lib/http/app/createApp.js.map +1 -0
- package/lib/http/app/devRoutes.d.ts +31 -0
- package/lib/http/app/devRoutes.d.ts.map +1 -0
- package/lib/http/app/devRoutes.js +145 -0
- package/lib/http/app/devRoutes.js.map +1 -0
- package/lib/http/app/errors.d.ts +45 -0
- package/lib/http/app/errors.d.ts.map +1 -0
- package/lib/http/app/errors.js +93 -0
- package/lib/http/app/errors.js.map +1 -0
- package/lib/http/app/health.d.ts +66 -0
- package/lib/http/app/health.d.ts.map +1 -0
- package/lib/http/app/health.js +104 -0
- package/lib/http/app/health.js.map +1 -0
- package/lib/http/errors/AppError.d.ts +10 -0
- package/lib/http/errors/AppError.d.ts.map +1 -0
- package/lib/http/errors/AppError.js +19 -0
- package/lib/http/errors/AppError.js.map +1 -0
- package/lib/http/errors/AuthenticationError.d.ts +8 -0
- package/lib/http/errors/AuthenticationError.d.ts.map +1 -0
- package/lib/http/errors/AuthenticationError.js +10 -0
- package/lib/http/errors/AuthenticationError.js.map +1 -0
- package/lib/http/errors/AuthorizationError.d.ts +8 -0
- package/lib/http/errors/AuthorizationError.d.ts.map +1 -0
- package/lib/http/errors/AuthorizationError.js +10 -0
- package/lib/http/errors/AuthorizationError.js.map +1 -0
- package/lib/http/errors/BadRequestError.d.ts +8 -0
- package/lib/http/errors/BadRequestError.d.ts.map +1 -0
- package/lib/http/errors/BadRequestError.js +10 -0
- package/lib/http/errors/BadRequestError.js.map +1 -0
- package/lib/http/errors/ConflictError.d.ts +8 -0
- package/lib/http/errors/ConflictError.d.ts.map +1 -0
- package/lib/http/errors/ConflictError.js +10 -0
- package/lib/http/errors/ConflictError.js.map +1 -0
- package/lib/http/errors/InternalServerError.d.ts +8 -0
- package/lib/http/errors/InternalServerError.d.ts.map +1 -0
- package/lib/http/errors/InternalServerError.js +10 -0
- package/lib/http/errors/InternalServerError.js.map +1 -0
- package/lib/http/errors/NotFoundError.d.ts +8 -0
- package/lib/http/errors/NotFoundError.d.ts.map +1 -0
- package/lib/http/errors/NotFoundError.js +10 -0
- package/lib/http/errors/NotFoundError.js.map +1 -0
- package/lib/http/errors/RateLimitError.d.ts +13 -0
- package/lib/http/errors/RateLimitError.d.ts.map +1 -0
- package/lib/http/errors/RateLimitError.js +16 -0
- package/lib/http/errors/RateLimitError.js.map +1 -0
- package/lib/http/errors/ServiceUnavailableError.d.ts +8 -0
- package/lib/http/errors/ServiceUnavailableError.d.ts.map +1 -0
- package/lib/http/errors/ServiceUnavailableError.js +10 -0
- package/lib/http/errors/ServiceUnavailableError.js.map +1 -0
- package/lib/http/errors/ValidationError.d.ts +9 -0
- package/lib/http/errors/ValidationError.d.ts.map +1 -0
- package/lib/http/errors/ValidationError.js +12 -0
- package/lib/http/errors/ValidationError.js.map +1 -0
- package/lib/http/errors/index.d.ts +11 -0
- package/lib/http/errors/index.d.ts.map +1 -0
- package/lib/http/errors/index.js +12 -0
- package/lib/http/errors/index.js.map +1 -0
- package/lib/http/middleware/apiKey.d.ts +9 -0
- package/lib/http/middleware/apiKey.d.ts.map +1 -0
- package/lib/http/middleware/apiKey.js +71 -0
- package/lib/http/middleware/apiKey.js.map +1 -0
- package/lib/http/middleware/csrf.d.ts +63 -0
- package/lib/http/middleware/csrf.d.ts.map +1 -0
- package/lib/http/middleware/csrf.js +169 -0
- package/lib/http/middleware/csrf.js.map +1 -0
- package/lib/http/middleware/httpLogger.d.ts +39 -0
- package/lib/http/middleware/httpLogger.d.ts.map +1 -0
- package/lib/http/middleware/httpLogger.js +118 -0
- package/lib/http/middleware/httpLogger.js.map +1 -0
- package/lib/http/middleware/idempotency.d.ts +48 -0
- package/lib/http/middleware/idempotency.d.ts.map +1 -0
- package/lib/http/middleware/idempotency.js +180 -0
- package/lib/http/middleware/idempotency.js.map +1 -0
- package/lib/http/middleware/rateLimit.d.ts +105 -0
- package/lib/http/middleware/rateLimit.d.ts.map +1 -0
- package/lib/http/middleware/rateLimit.js +84 -0
- package/lib/http/middleware/rateLimit.js.map +1 -0
- package/lib/http/middleware/requireAuth.d.ts +58 -0
- package/lib/http/middleware/requireAuth.d.ts.map +1 -0
- package/lib/http/middleware/requireAuth.js +63 -0
- package/lib/http/middleware/requireAuth.js.map +1 -0
- package/lib/http/middleware/requirePermission.d.ts +23 -0
- package/lib/http/middleware/requirePermission.d.ts.map +1 -0
- package/lib/http/middleware/requirePermission.js +50 -0
- package/lib/http/middleware/requirePermission.js.map +1 -0
- package/lib/http/middleware/resolveTenant.d.ts +41 -0
- package/lib/http/middleware/resolveTenant.d.ts.map +1 -0
- package/lib/http/middleware/resolveTenant.js +17 -0
- package/lib/http/middleware/resolveTenant.js.map +1 -0
- package/lib/http/streaming/sse.d.ts +31 -0
- package/lib/http/streaming/sse.d.ts.map +1 -0
- package/lib/http/streaming/sse.js +11 -0
- package/lib/http/streaming/sse.js.map +1 -0
- package/lib/http/streaming/streamLLM.d.ts +94 -0
- package/lib/http/streaming/streamLLM.d.ts.map +1 -0
- package/lib/http/streaming/streamLLM.js +94 -0
- package/lib/http/streaming/streamLLM.js.map +1 -0
- package/lib/http/utils/authCookies.d.ts +74 -0
- package/lib/http/utils/authCookies.d.ts.map +1 -0
- package/lib/http/utils/authCookies.js +102 -0
- package/lib/http/utils/authCookies.js.map +1 -0
- package/lib/http/utils/clientIp.d.ts +85 -0
- package/lib/http/utils/clientIp.d.ts.map +1 -0
- package/lib/http/utils/clientIp.js +249 -0
- package/lib/http/utils/clientIp.js.map +1 -0
- package/lib/http/utils/index.d.ts +4 -0
- package/lib/http/utils/index.d.ts.map +1 -0
- package/lib/http/utils/index.js +5 -0
- package/lib/http/utils/index.js.map +1 -0
- package/lib/http/utils/metaTags.d.ts +49 -0
- package/lib/http/utils/metaTags.d.ts.map +1 -0
- package/lib/http/utils/metaTags.js +32 -0
- package/lib/http/utils/metaTags.js.map +1 -0
- package/lib/http/utils/publicSuffix.d.ts +34 -0
- package/lib/http/utils/publicSuffix.d.ts.map +1 -0
- package/lib/http/utils/publicSuffix.js +276 -0
- package/lib/http/utils/publicSuffix.js.map +1 -0
- package/lib/http/utils/uploads.d.ts +92 -0
- package/lib/http/utils/uploads.d.ts.map +1 -0
- package/lib/http/utils/uploads.js +126 -0
- package/lib/http/utils/uploads.js.map +1 -0
- package/lib/http/utils/validate.d.ts +41 -0
- package/lib/http/utils/validate.d.ts.map +1 -0
- package/lib/http/utils/validate.js +55 -0
- package/lib/http/utils/validate.js.map +1 -0
- package/lib/http/utils/webhooks.d.ts +63 -0
- package/lib/http/utils/webhooks.d.ts.map +1 -0
- package/lib/http/utils/webhooks.js +133 -0
- package/lib/http/utils/webhooks.js.map +1 -0
- package/lib/http/ws/auth.d.ts +46 -0
- package/lib/http/ws/auth.d.ts.map +1 -0
- package/lib/http/ws/auth.js +123 -0
- package/lib/http/ws/auth.js.map +1 -0
- package/lib/index.d.ts +80 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +61 -0
- package/lib/index.js.map +1 -0
- package/lib/kernel/index.d.ts +3 -0
- package/lib/kernel/index.d.ts.map +1 -0
- package/lib/kernel/index.js +3 -0
- package/lib/kernel/index.js.map +1 -0
- package/lib/libraries/.gitkeep +0 -0
- package/lib/libraries/ai/CHANGELOG.md +17 -0
- package/lib/libraries/ai/README.md +164 -0
- package/lib/libraries/ai/adapters/AnthropicAdapter.ts +382 -0
- package/lib/libraries/ai/adapters/LLMProvider.ts +76 -0
- package/lib/libraries/ai/adapters/OpenAIAdapter.ts +466 -0
- package/lib/libraries/ai/experimental/index.ts +20 -0
- package/lib/libraries/ai/index.ts +128 -0
- package/lib/libraries/ai/library.json +19 -0
- package/lib/libraries/ai/loop/agentLoop.ts +377 -0
- package/lib/libraries/ai/prompts/definePrompt.ts +0 -0
- package/lib/libraries/ai/rag/retrieveAndAnswer.ts +177 -0
- package/lib/libraries/ai/streaming/streamLLM.ts +94 -0
- package/lib/libraries/ai/structured/completeStructured.ts +143 -0
- package/lib/libraries/ai/tokens/Tokenizer.ts +216 -0
- package/lib/libraries/ai/tools/defineTool.ts +208 -0
- package/lib/libraries/ai/types.ts +260 -0
- package/lib/libraries/chunking/ChunkingService.ts +103 -0
- package/lib/libraries/chunking/README.md +104 -0
- package/lib/libraries/chunking/docs/api-reference.md +549 -0
- package/lib/libraries/chunking/docs/best-practices.md +321 -0
- package/lib/libraries/chunking/docs/code-organization.md +538 -0
- package/lib/libraries/chunking/docs/configuration.md +418 -0
- package/lib/libraries/chunking/docs/index.md +236 -0
- package/lib/libraries/chunking/docs/quick-start.md +298 -0
- package/lib/libraries/chunking/docs/strategies.md +407 -0
- package/lib/libraries/chunking/docs/summary.md +258 -0
- package/lib/libraries/chunking/docs/troubleshooting.md +527 -0
- package/lib/libraries/chunking/index.ts +17 -0
- package/lib/libraries/chunking/library.json +16 -0
- package/lib/libraries/chunking/strategies/ChunkingStrategy.ts +13 -0
- package/lib/libraries/chunking/strategies/FixedLengthChunkingStrategy.ts +88 -0
- package/lib/libraries/chunking/strategies/SemanticChunkingStrategy.ts +389 -0
- package/lib/libraries/chunking/strategies/SlidingWindowChunkingStrategy.ts +258 -0
- package/lib/libraries/chunking/types/index.ts +63 -0
- package/lib/libraries/email/EmailService.ts +591 -0
- package/lib/libraries/email/README.md +777 -0
- package/lib/libraries/email/TemplateRegistry.ts +148 -0
- package/lib/libraries/email/TemplateRenderer.ts +273 -0
- package/lib/libraries/email/index.ts +4 -0
- package/lib/libraries/email/initializeEmailService.ts +60 -0
- package/lib/libraries/email/library.json +12 -0
- package/lib/libraries/email/types/EmailAddress.ts +4 -0
- package/lib/libraries/email/types/EmailAttachment.ts +11 -0
- package/lib/libraries/email/types/EmailTag.ts +9 -0
- package/lib/libraries/email/types/EmailTemplate.ts +124 -0
- package/lib/libraries/email/types/SendBatchEmailsOptions.ts +9 -0
- package/lib/libraries/email/types/SendBatchEmailsResult.ts +9 -0
- package/lib/libraries/email/types/SendEmailOptions.ts +33 -0
- package/lib/libraries/email/types/SendEmailResult.ts +9 -0
- package/lib/libraries/email/types/index.ts +8 -0
- package/lib/libraries/weaviate/QueryBuilder.ts +292 -0
- package/lib/libraries/weaviate/README.md +284 -0
- package/lib/libraries/weaviate/Weaviate.ts +1419 -0
- package/lib/libraries/weaviate/WeaviateConfig.ts +268 -0
- package/lib/libraries/weaviate/docs/.gitkeep +1 -0
- package/lib/libraries/weaviate/docs/API_REFERENCE.md +1067 -0
- package/lib/libraries/weaviate/docs/CONFIGURATION.md +320 -0
- package/lib/libraries/weaviate/docs/ERROR_HANDLING.md +271 -0
- package/lib/libraries/weaviate/docs/MULTI_TENANCY.md +348 -0
- package/lib/libraries/weaviate/docs/PERFORMANCE.md +602 -0
- package/lib/libraries/weaviate/docs/PLUGIN_DEVELOPMENT.md +547 -0
- package/lib/libraries/weaviate/docs/QUICK_START.md +443 -0
- package/lib/libraries/weaviate/docs/README.md +269 -0
- package/lib/libraries/weaviate/docs/SCHEMA_GUIDE.md +630 -0
- package/lib/libraries/weaviate/docs/WEAVIATE_README.md +520 -0
- package/lib/libraries/weaviate/errors/WeaviateError.ts +92 -0
- package/lib/libraries/weaviate/errors/index.ts +1 -0
- package/lib/libraries/weaviate/index.ts +35 -0
- package/lib/libraries/weaviate/library.json +26 -0
- package/lib/libraries/weaviate/plugins/index.ts +2 -0
- package/lib/libraries/weaviate/schemas/SchemaRegistry.ts +284 -0
- package/lib/libraries/weaviate/schemas/index.ts +2 -0
- package/lib/libraries/weaviate/types/BM25SearchOptions.ts +9 -0
- package/lib/libraries/weaviate/types/ExtendedWeaviateClient.ts +7 -0
- package/lib/libraries/weaviate/types/HybridSearchOptions.ts +11 -0
- package/lib/libraries/weaviate/types/MultiTenancy.ts +81 -0
- package/lib/libraries/weaviate/types/Plugin.ts +17 -0
- package/lib/libraries/weaviate/types/SchemaConfig.ts +25 -0
- package/lib/libraries/weaviate/types/SearchMethod.ts +8 -0
- package/lib/libraries/weaviate/types/SearchOptions.ts +16 -0
- package/lib/libraries/weaviate/types/SearchRequest.ts +19 -0
- package/lib/libraries/weaviate/types/SearchResult.ts +16 -0
- package/lib/libraries/weaviate/types/SearchableField.ts +87 -0
- package/lib/libraries/weaviate/types/VectorIndexConfig.ts +19 -0
- package/lib/libraries/weaviate/types/VectorSearchOptions.ts +18 -0
- package/lib/libraries/weaviate/types/VectorizerConfig.ts +8 -0
- package/lib/libraries/weaviate/types/index.ts +29 -0
- package/lib/libraries/weaviate/utils/TenantContextManager.ts +94 -0
- package/lib/modules/accounts/index.ts +47 -0
- package/lib/modules/accounts/module.json +15 -0
- package/lib/modules/accounts/permissions.ts +33 -0
- package/lib/modules/accounts/routes.ts +200 -0
- package/lib/modules/accounts/schema.ts +219 -0
- package/lib/modules/accounts/services/AccountService.ts +44 -0
- package/lib/modules/accounts/validation/schemas.ts +70 -0
- package/lib/modules/oauth/adapters/GmailOAuthAdapter.ts +35 -0
- package/lib/modules/oauth/adapters/GoogleOAuthAdapter.ts +278 -0
- package/lib/modules/oauth/adapters/MicrosoftOAuthAdapter.ts +269 -0
- package/lib/modules/oauth/adapters/MockOAuthAdapter.ts +138 -0
- package/lib/modules/oauth/adapters/OAuthAdapter.ts +211 -0
- package/lib/modules/oauth/adapters/OAuthAdapterRegistry.ts +77 -0
- package/lib/modules/oauth/adapters/OutlookOAuthAdapter.ts +38 -0
- package/lib/modules/oauth/index.ts +79 -0
- package/lib/modules/oauth/module.json +23 -0
- package/lib/modules/oauth/permissions.ts +39 -0
- package/lib/modules/oauth/routes.ts +497 -0
- package/lib/modules/oauth/schema.ts +127 -0
- package/lib/modules/oauth/services/OAuthConnectionService.ts +147 -0
- package/lib/modules/oauth/state.ts +234 -0
- package/lib/modules/oauth/stateJtiCache.ts +85 -0
- package/lib/modules/oauth/verifyIdToken.ts +178 -0
- package/lib/modules/outbox/EventEmitter.ts +106 -0
- package/lib/modules/outbox/OutboxDrainer.ts +163 -0
- package/lib/modules/outbox/handlers/EventHandler.ts +115 -0
- package/lib/modules/outbox/index.ts +43 -0
- package/lib/modules/outbox/module.json +15 -0
- package/lib/modules/outbox/permissions.ts +36 -0
- package/lib/modules/outbox/routes.ts +219 -0
- package/lib/modules/outbox/schema.ts +207 -0
- package/lib/modules/outbox/services/OutboxEventService.ts +223 -0
- package/lib/observability/logger.d.ts +30 -0
- package/lib/observability/logger.d.ts.map +1 -0
- package/lib/observability/logger.js +110 -0
- package/lib/observability/logger.js.map +1 -0
- package/lib/observability/metrics.d.ts +3 -0
- package/lib/observability/metrics.d.ts.map +1 -0
- package/lib/observability/metrics.js +3 -0
- package/lib/observability/metrics.js.map +1 -0
- package/lib/observability/otel.d.ts +3 -0
- package/lib/observability/otel.d.ts.map +1 -0
- package/lib/observability/otel.js +3 -0
- package/lib/observability/otel.js.map +1 -0
- package/lib/observability/safeErrorFields.d.ts +14 -0
- package/lib/observability/safeErrorFields.d.ts.map +1 -0
- package/lib/observability/safeErrorFields.js +14 -0
- package/lib/observability/safeErrorFields.js.map +1 -0
- package/lib/observability/tracing.d.ts +3 -0
- package/lib/observability/tracing.d.ts.map +1 -0
- package/lib/observability/tracing.js +3 -0
- package/lib/observability/tracing.js.map +1 -0
- package/lib/package.json +4 -0
- package/lib/testing.d.ts +3 -0
- package/lib/testing.d.ts.map +1 -0
- package/lib/testing.js +3 -0
- package/lib/testing.js.map +1 -0
- package/package.json +193 -3
- package/templates/default/.claude/agents/qa-brief.md +106 -0
- package/templates/default/.claude/commands/sherpa-brief.md +23 -0
- package/templates/default/.cursor/rules/api.mdc +80 -0
- package/templates/default/.cursor/rules/commits.mdc +88 -0
- package/templates/default/.cursor/rules/security.mdc +92 -0
- package/templates/default/.cursor/rules/testing.mdc +123 -0
- package/templates/default/.cursor/rules/typescript.mdc +86 -0
- package/templates/default/.env.example +79 -0
- package/templates/default/.github/workflows/sherpa-brief.yml +180 -0
- package/templates/default/AGENTS.md +166 -0
- package/templates/default/CLAUDE.md +17 -0
- package/templates/default/README.md +97 -0
- package/templates/default/_gitignore +27 -0
- package/templates/default/docker-compose.yml +83 -0
- package/templates/default/drizzle.config.ts +34 -0
- package/templates/default/eslint.config.mjs +106 -0
- package/templates/default/package.json +57 -0
- package/templates/default/ship-api.config.ts +21 -0
- package/templates/default/src/client.ts +34 -0
- package/templates/default/src/config/modules.ts +23 -0
- package/templates/default/src/db/schema.ts +26 -0
- package/templates/default/src/environment.ts +81 -0
- package/templates/default/src/index.test.ts +53 -0
- package/templates/default/src/index.ts +11 -0
- package/templates/default/src/instrumentation.ts +13 -0
- package/templates/default/src/server.ts +90 -0
- package/templates/default/tsconfig.json +24 -0
|
@@ -0,0 +1,1419 @@
|
|
|
1
|
+
import Logger from "@sixthsummit/ship-api/app/utils/Logger";
|
|
2
|
+
import { WeaviateError, WeaviateErrorCode } from "./errors/index.js";
|
|
3
|
+
import { createQueryBuilder, type QueryBuilder, QueryPatterns } from "./QueryBuilder.js";
|
|
4
|
+
import SchemaRegistry from "./schemas/SchemaRegistry.js";
|
|
5
|
+
import type {
|
|
6
|
+
BM25SearchOptions,
|
|
7
|
+
CreateTenantOptions,
|
|
8
|
+
ExtendedWeaviateClient,
|
|
9
|
+
HybridSearchOptions,
|
|
10
|
+
Plugin,
|
|
11
|
+
SchemaConfig,
|
|
12
|
+
SearchableField,
|
|
13
|
+
SearchOptions,
|
|
14
|
+
SearchRequest,
|
|
15
|
+
SearchResult,
|
|
16
|
+
Tenant,
|
|
17
|
+
TenantManagementResult,
|
|
18
|
+
TenantStats,
|
|
19
|
+
UpdateTenantOptions,
|
|
20
|
+
Vectorizer,
|
|
21
|
+
VectorSearchOptions,
|
|
22
|
+
} from "./types/index.js";
|
|
23
|
+
import TenantContextManager from "./utils/TenantContextManager.js";
|
|
24
|
+
import { WeaviateConfigService } from "./WeaviateConfig.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Options for initializing a Weaviate instance
|
|
28
|
+
*/
|
|
29
|
+
export interface WeaviateOptions {
|
|
30
|
+
/** Optional schema registry to use. If not provided, a new one will be created. */
|
|
31
|
+
registry?: SchemaRegistry;
|
|
32
|
+
/** Optional custom plugins to register. */
|
|
33
|
+
plugins?: Plugin[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Main Weaviate class for vector database operations
|
|
38
|
+
*/
|
|
39
|
+
export default class Weaviate {
|
|
40
|
+
private client: ExtendedWeaviateClient | null = null;
|
|
41
|
+
private readonly registry: SchemaRegistry;
|
|
42
|
+
private readonly defaultLimit = 25;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create a new Weaviate instance
|
|
46
|
+
* Client will be lazily initialized on first use
|
|
47
|
+
* @param options - Optional configuration for the instance
|
|
48
|
+
* @example
|
|
49
|
+
* // Simple usage
|
|
50
|
+
* const weaviate = new Weaviate();
|
|
51
|
+
*
|
|
52
|
+
* // With custom plugins
|
|
53
|
+
* const weaviate = new Weaviate({
|
|
54
|
+
* plugins: [new MyCustomPlugin()]
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // For testing with mock registry
|
|
58
|
+
* const mockRegistry = new SchemaRegistry();
|
|
59
|
+
* const weaviate = new Weaviate({ registry: mockRegistry });
|
|
60
|
+
*/
|
|
61
|
+
constructor(options: WeaviateOptions = {}) {
|
|
62
|
+
// Use provided registry or create new one
|
|
63
|
+
this.registry = options.registry || new SchemaRegistry();
|
|
64
|
+
|
|
65
|
+
// Register custom plugins if provided
|
|
66
|
+
if (options.plugins) {
|
|
67
|
+
options.plugins.forEach((plugin) => {
|
|
68
|
+
this.registry.registerPlugin(plugin);
|
|
69
|
+
});
|
|
70
|
+
Logger.log(`Weaviate initialized with ${options.plugins.length} custom plugin(s)`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Logger.log("Weaviate instance created (client will be initialized on first use)");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get the Weaviate client, lazily initializing if needed
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
private async getClient(skipHealthCheck = false): Promise<ExtendedWeaviateClient> {
|
|
81
|
+
if (!this.client) {
|
|
82
|
+
this.client = (await WeaviateConfigService.getClient(
|
|
83
|
+
skipHealthCheck,
|
|
84
|
+
)) as ExtendedWeaviateClient;
|
|
85
|
+
}
|
|
86
|
+
return this.client;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Reset the client connection (useful after connection errors)
|
|
91
|
+
* Next getClient() call will re-establish the connection
|
|
92
|
+
*/
|
|
93
|
+
resetClient(): void {
|
|
94
|
+
this.client = null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Initialize Weaviate schemas (call this during app startup)
|
|
99
|
+
*
|
|
100
|
+
* This method registers the schemas in the registry and creates them in Weaviate.
|
|
101
|
+
*
|
|
102
|
+
* @param schemas - Array of schema configurations to initialize
|
|
103
|
+
* @example
|
|
104
|
+
* const schemas = [PersonSchema, OrganizationSchema];
|
|
105
|
+
* await weaviate.initializeSchemas(schemas);
|
|
106
|
+
*/
|
|
107
|
+
async initializeSchemas(schemas: SchemaConfig[]): Promise<void> {
|
|
108
|
+
try {
|
|
109
|
+
Logger.log("Initializing schemas...");
|
|
110
|
+
|
|
111
|
+
// Register schemas in the registry
|
|
112
|
+
schemas.forEach((schema) => {
|
|
113
|
+
this.registry.registerSchema(schema);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Create schemas in Weaviate database
|
|
117
|
+
for (const schema of schemas) {
|
|
118
|
+
await this.createSchema(schema);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
Logger.log(`Initialized ${schemas.length} schemas successfully`);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
Logger.log("Error initializing schemas:", error);
|
|
124
|
+
throw WeaviateError.from(
|
|
125
|
+
error,
|
|
126
|
+
WeaviateErrorCode.SCHEMA_CREATION_ERROR,
|
|
127
|
+
"Failed to initialize schemas",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a schema in Weaviate
|
|
134
|
+
* Note: Schema validation happens in SchemaRegistry.registerSchema (fail-fast principle)
|
|
135
|
+
*/
|
|
136
|
+
async createSchema(schema: SchemaConfig): Promise<void> {
|
|
137
|
+
try {
|
|
138
|
+
// Convert to Weaviate format
|
|
139
|
+
const weaviateSchema = this.convertToWeaviateSchema(schema);
|
|
140
|
+
|
|
141
|
+
const client = await this.getClient();
|
|
142
|
+
await client.collections.createFromSchema(weaviateSchema);
|
|
143
|
+
Logger.log(`Schema created for class: ${schema.name}`);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
if (error.message?.includes("already exists")) {
|
|
146
|
+
Logger.log(`Schema for class ${schema.name} already exists`);
|
|
147
|
+
} else {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Add a property to an existing schema/collection
|
|
155
|
+
*
|
|
156
|
+
* Use this for schema migrations when you need to add new fields
|
|
157
|
+
* without recreating the entire collection.
|
|
158
|
+
*
|
|
159
|
+
* @param collectionName - Name of the collection (e.g., "Person")
|
|
160
|
+
* @param property - Property configuration matching SearchableField interface
|
|
161
|
+
* @throws Error if the property already exists or collection not found
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* await weaviate.addProperty("Person", {
|
|
165
|
+
* name: "networkUserIds",
|
|
166
|
+
* dataType: ["text[]"],
|
|
167
|
+
* description: "User IDs who have this person in their network",
|
|
168
|
+
* indexInverted: true,
|
|
169
|
+
* });
|
|
170
|
+
*/
|
|
171
|
+
async addProperty(collectionName: string, property: SearchableField): Promise<void> {
|
|
172
|
+
try {
|
|
173
|
+
const client = await this.getClient();
|
|
174
|
+
const collection = client.collections.get(collectionName);
|
|
175
|
+
|
|
176
|
+
// Convert SearchableField to Weaviate v3 property format
|
|
177
|
+
// Note: collection.config.addProperty expects dataType as a single string, not an array
|
|
178
|
+
// e.g., "text[]" not ["text[]"]
|
|
179
|
+
const dataType = Array.isArray(property.dataType) ? property.dataType[0] : property.dataType;
|
|
180
|
+
|
|
181
|
+
const weaviateProperty: Record<string, unknown> = {
|
|
182
|
+
name: property.name,
|
|
183
|
+
dataType,
|
|
184
|
+
description: property.description,
|
|
185
|
+
// Index settings - v4 uses indexFilterable/indexSearchable
|
|
186
|
+
// indexInverted is legacy but may still be accepted by some versions
|
|
187
|
+
indexFilterable: property.indexFilterable ?? property.indexInverted ?? true,
|
|
188
|
+
indexSearchable: property.indexSearchable ?? false,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (property.tokenization !== undefined) {
|
|
192
|
+
weaviateProperty.tokenization = property.tokenization;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await collection.config.addProperty(weaviateProperty as any);
|
|
196
|
+
Logger.log(`Property '${property.name}' added to collection '${collectionName}'`);
|
|
197
|
+
} catch (error: any) {
|
|
198
|
+
if (error.message?.includes("already exists")) {
|
|
199
|
+
Logger.log(`Property '${property.name}' already exists in '${collectionName}'`);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
Logger.log(`Error adding property '${property.name}' to '${collectionName}':`, error);
|
|
203
|
+
throw WeaviateError.from(
|
|
204
|
+
error,
|
|
205
|
+
WeaviateErrorCode.SCHEMA_CREATION_ERROR,
|
|
206
|
+
`Failed to add property '${property.name}' to collection '${collectionName}'`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Type guard to check if vectorizer is a string ("none")
|
|
213
|
+
*/
|
|
214
|
+
private isStringVectorizer(vectorizer: Vectorizer | undefined): vectorizer is "none" {
|
|
215
|
+
return typeof vectorizer === "string";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Convert schema config to Weaviate format
|
|
220
|
+
*/
|
|
221
|
+
private convertToWeaviateSchema(schema: SchemaConfig): any {
|
|
222
|
+
const weaviateSchema: any = {
|
|
223
|
+
class: schema.name,
|
|
224
|
+
description: schema.description,
|
|
225
|
+
vectorizer: this.isStringVectorizer(schema.vectorizer)
|
|
226
|
+
? schema.vectorizer
|
|
227
|
+
: schema.vectorizer?.name,
|
|
228
|
+
vectorIndexConfig: schema.vectorIndexConfig,
|
|
229
|
+
properties: schema.properties.map((prop) => ({
|
|
230
|
+
name: prop.name,
|
|
231
|
+
dataType: prop.dataType,
|
|
232
|
+
description: prop.description,
|
|
233
|
+
indexInverted: prop.indexInverted,
|
|
234
|
+
indexFilterable: prop.indexFilterable,
|
|
235
|
+
indexSearchable: prop.indexSearchable,
|
|
236
|
+
})),
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Add multi-tenancy configuration if enabled
|
|
240
|
+
if (schema.multiTenancy?.enabled) {
|
|
241
|
+
weaviateSchema.multiTenancyConfig = {
|
|
242
|
+
enabled: true,
|
|
243
|
+
autoTenantCreation: schema.multiTenancy.autoTenantCreation ?? true,
|
|
244
|
+
autoTenantActivation: schema.multiTenancy.autoTenantActivation ?? true,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return weaviateSchema;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ==================== SEARCH METHODS ====================
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Generic search method with type-safe discriminated union
|
|
255
|
+
*
|
|
256
|
+
* @param schemaName - Name of the schema to search
|
|
257
|
+
* @param request - Search request with method and options
|
|
258
|
+
* @returns Search results with matching objects
|
|
259
|
+
* @example
|
|
260
|
+
* // Vector search
|
|
261
|
+
* const results = await weaviate.search('Person', {
|
|
262
|
+
* method: 'vector',
|
|
263
|
+
* options: { nearText: { concepts: ['engineer'] }, limit: 10 }
|
|
264
|
+
* });
|
|
265
|
+
*
|
|
266
|
+
* // BM25 search
|
|
267
|
+
* const results = await weaviate.search('Person', {
|
|
268
|
+
* method: 'bm25',
|
|
269
|
+
* options: { bm25: { query: 'John Smith' }, limit: 10 }
|
|
270
|
+
* });
|
|
271
|
+
*
|
|
272
|
+
* // Hybrid search
|
|
273
|
+
* const results = await weaviate.search('Person', {
|
|
274
|
+
* method: 'hybrid',
|
|
275
|
+
* options: { hybrid: { query: 'engineer', alpha: 0.5 }, limit: 10 }
|
|
276
|
+
* });
|
|
277
|
+
*/
|
|
278
|
+
async search<T = any>(schemaName: string, request: SearchRequest): Promise<SearchResult<T>> {
|
|
279
|
+
try {
|
|
280
|
+
// Validate schema exists
|
|
281
|
+
if (!this.registry.hasSchema(schemaName)) {
|
|
282
|
+
throw new Error(`Schema '${schemaName}' not found in registry`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Get schema plugins
|
|
286
|
+
const plugins = this.registry.getSchemaPlugins(schemaName);
|
|
287
|
+
|
|
288
|
+
// Apply beforeSearch hooks
|
|
289
|
+
let processedOptions = request.options;
|
|
290
|
+
for (const plugin of plugins) {
|
|
291
|
+
if (plugin.hooks.beforeSearch) {
|
|
292
|
+
processedOptions = plugin.hooks.beforeSearch(schemaName, processedOptions);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Execute search based on method (TypeScript properly narrows types here)
|
|
297
|
+
let result: SearchResult<T>;
|
|
298
|
+
switch (request.method) {
|
|
299
|
+
case "vector":
|
|
300
|
+
result = await this.executeVectorSearch(schemaName, processedOptions);
|
|
301
|
+
break;
|
|
302
|
+
case "bm25":
|
|
303
|
+
result = await this.executeBM25Search(schemaName, processedOptions);
|
|
304
|
+
break;
|
|
305
|
+
case "hybrid":
|
|
306
|
+
result = await this.executeHybridSearch(schemaName, processedOptions);
|
|
307
|
+
break;
|
|
308
|
+
default:
|
|
309
|
+
// Exhaustiveness check - TypeScript ensures all methods are handled
|
|
310
|
+
throw new Error(`Unsupported search method`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Apply afterSearch hooks
|
|
314
|
+
for (const plugin of plugins) {
|
|
315
|
+
if (plugin.hooks.afterSearch) {
|
|
316
|
+
result = plugin.hooks.afterSearch(schemaName, result);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return result;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
Logger.log(`Error searching ${schemaName} with ${request.method}:`, error);
|
|
323
|
+
throw WeaviateError.from(
|
|
324
|
+
error,
|
|
325
|
+
WeaviateErrorCode.SEARCH_ERROR,
|
|
326
|
+
`Failed to search ${schemaName} with method ${request.method}`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Check if filter object is a plain object (our format) that needs conversion
|
|
333
|
+
*
|
|
334
|
+
* @param obj - Filter object to check
|
|
335
|
+
* @returns true if plain object, false if already a FilterValue
|
|
336
|
+
*/
|
|
337
|
+
private isPlainFilterObject(obj: any): boolean {
|
|
338
|
+
return (
|
|
339
|
+
obj &&
|
|
340
|
+
typeof obj === "object" &&
|
|
341
|
+
Object.hasOwn(obj, "path") &&
|
|
342
|
+
Object.hasOwn(obj, "operator") &&
|
|
343
|
+
Object.hasOwn(obj, "valueString")
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Build Weaviate FilterValue from plain object using collection.filter API
|
|
349
|
+
*
|
|
350
|
+
* Converts our simple filter format to Weaviate v3 FilterValue objects.
|
|
351
|
+
* This enables proper filtering in Weaviate instead of post-filtering in application.
|
|
352
|
+
*
|
|
353
|
+
* @param collection - Weaviate collection with filter builder
|
|
354
|
+
* @param filterObj - Plain filter object { path, operator, valueString }
|
|
355
|
+
* @returns FilterValue object for Weaviate v3 client, or undefined on error
|
|
356
|
+
*/
|
|
357
|
+
private buildFilter(collection: any, filterObj: any): any {
|
|
358
|
+
// If not a plain object, assume it's already a FilterValue
|
|
359
|
+
if (!this.isPlainFilterObject(filterObj)) {
|
|
360
|
+
return filterObj;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Validate collection has filter API
|
|
364
|
+
if (!collection || !collection.filter || !collection.filter.byProperty) {
|
|
365
|
+
Logger.log("Warning: Collection does not have filter.byProperty API, skipping filter");
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const { path, operator, valueString } = filterObj;
|
|
370
|
+
|
|
371
|
+
// Validate path
|
|
372
|
+
if (!path || !Array.isArray(path) || path.length === 0) {
|
|
373
|
+
Logger.log("Warning: Filter has invalid or empty path, skipping filter");
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const property = path[0]; // Use first element of path array
|
|
378
|
+
|
|
379
|
+
// Validate valueString
|
|
380
|
+
if (valueString === undefined || valueString === null) {
|
|
381
|
+
Logger.log(`Warning: Filter has no valueString for property ${property}, skipping filter`);
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Build filter using collection.filter API
|
|
386
|
+
try {
|
|
387
|
+
switch (operator) {
|
|
388
|
+
case "ContainsAny":
|
|
389
|
+
Logger.log(
|
|
390
|
+
`Building ContainsAny filter for ${property} with ${Array.isArray(valueString) ? valueString.length : 1} values`,
|
|
391
|
+
);
|
|
392
|
+
return collection.filter.byProperty(property).containsAny(valueString);
|
|
393
|
+
|
|
394
|
+
case "ContainsAll":
|
|
395
|
+
return collection.filter.byProperty(property).containsAll(valueString);
|
|
396
|
+
|
|
397
|
+
case "ContainsNone":
|
|
398
|
+
return collection.filter.byProperty(property).containsNone(valueString);
|
|
399
|
+
|
|
400
|
+
case "Equal":
|
|
401
|
+
return collection.filter.byProperty(property).equal(valueString);
|
|
402
|
+
|
|
403
|
+
case "NotEqual":
|
|
404
|
+
return collection.filter.byProperty(property).notEqual(valueString);
|
|
405
|
+
|
|
406
|
+
case "GreaterThan":
|
|
407
|
+
return collection.filter.byProperty(property).greaterThan(valueString);
|
|
408
|
+
|
|
409
|
+
case "GreaterThanEqual":
|
|
410
|
+
return collection.filter.byProperty(property).greaterOrEqual(valueString);
|
|
411
|
+
|
|
412
|
+
case "LessThan":
|
|
413
|
+
return collection.filter.byProperty(property).lessThan(valueString);
|
|
414
|
+
|
|
415
|
+
case "LessThanEqual":
|
|
416
|
+
return collection.filter.byProperty(property).lessOrEqual(valueString);
|
|
417
|
+
|
|
418
|
+
case "Like":
|
|
419
|
+
return collection.filter.byProperty(property).like(valueString);
|
|
420
|
+
|
|
421
|
+
case "IsNull":
|
|
422
|
+
return collection.filter.byProperty(property).isNull(valueString === true);
|
|
423
|
+
|
|
424
|
+
// Compound filters not supported in Phase 1
|
|
425
|
+
case "And":
|
|
426
|
+
case "Or":
|
|
427
|
+
case "Not":
|
|
428
|
+
Logger.log(`Warning: Compound filter ${operator} not yet supported, skipping filter`);
|
|
429
|
+
return undefined;
|
|
430
|
+
|
|
431
|
+
default:
|
|
432
|
+
Logger.log(`Warning: Unsupported filter operator: ${operator}, skipping filter`);
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
Logger.log(`Error building filter for ${property} with operator ${operator}:`, error);
|
|
437
|
+
Logger.log("Falling back to no filter");
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Execute vector search
|
|
444
|
+
*/
|
|
445
|
+
private async executeVectorSearch<T>(
|
|
446
|
+
schemaName: string,
|
|
447
|
+
options: VectorSearchOptions,
|
|
448
|
+
): Promise<SearchResult<T>> {
|
|
449
|
+
const client = await this.getClient();
|
|
450
|
+
let collection = client.collections.get(schemaName);
|
|
451
|
+
|
|
452
|
+
// Add tenant context if provided
|
|
453
|
+
if (options.tenant) {
|
|
454
|
+
collection = collection.withTenant(options.tenant.tenant);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Build filter using collection.filter API if plain object provided
|
|
458
|
+
const filters = options.where ? this.buildFilter(collection, options.where) : undefined;
|
|
459
|
+
|
|
460
|
+
const queryOptions: any = {
|
|
461
|
+
limit: options.limit || this.defaultLimit,
|
|
462
|
+
offset: options.offset || 0,
|
|
463
|
+
filters,
|
|
464
|
+
returnProperties: options.fields,
|
|
465
|
+
includeVector: options.includeVector,
|
|
466
|
+
returnMetadata: options.includeMetadata
|
|
467
|
+
? ["certainty", "distance", "explainScore"]
|
|
468
|
+
: undefined,
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
let result;
|
|
472
|
+
|
|
473
|
+
// Execute the appropriate search based on what's provided
|
|
474
|
+
if (options.nearText) {
|
|
475
|
+
const query = Array.isArray(options.nearText.concepts)
|
|
476
|
+
? options.nearText.concepts.join(" ")
|
|
477
|
+
: options.nearText.concepts || "";
|
|
478
|
+
result = await collection.query.nearText(query, queryOptions);
|
|
479
|
+
} else if (options.nearVector) {
|
|
480
|
+
result = await collection.query.nearVector(options.nearVector.vector, queryOptions);
|
|
481
|
+
} else if (options.nearObject) {
|
|
482
|
+
result = await collection.query.nearObject(options.nearObject.id, queryOptions);
|
|
483
|
+
} else {
|
|
484
|
+
// Fallback to fetchObjects if no vector search specified
|
|
485
|
+
result = await collection.query.fetchObjects(queryOptions);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
objects: (result.objects || []).map((obj) => ({
|
|
490
|
+
id: obj.uuid,
|
|
491
|
+
properties: obj.properties as T,
|
|
492
|
+
vector: this.extractVector(obj.vectors),
|
|
493
|
+
certainty: obj.metadata?.certainty,
|
|
494
|
+
distance: obj.metadata?.distance,
|
|
495
|
+
score: obj.metadata?.score,
|
|
496
|
+
metadata: obj.metadata,
|
|
497
|
+
})),
|
|
498
|
+
total: result.objects?.length || 0,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Execute BM25 search
|
|
504
|
+
*/
|
|
505
|
+
private async executeBM25Search<T>(
|
|
506
|
+
schemaName: string,
|
|
507
|
+
options: BM25SearchOptions,
|
|
508
|
+
): Promise<SearchResult<T>> {
|
|
509
|
+
const client = await this.getClient();
|
|
510
|
+
let collection = client.collections.get(schemaName);
|
|
511
|
+
|
|
512
|
+
// Add tenant context if provided
|
|
513
|
+
if (options.tenant) {
|
|
514
|
+
collection = collection.withTenant(options.tenant.tenant);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Build filter using collection.filter API if plain object provided
|
|
518
|
+
const filters = options.where ? this.buildFilter(collection, options.where) : undefined;
|
|
519
|
+
|
|
520
|
+
const queryOptions: any = {
|
|
521
|
+
limit: options.limit || this.defaultLimit,
|
|
522
|
+
offset: options.offset || 0,
|
|
523
|
+
filters,
|
|
524
|
+
returnProperties: options.fields,
|
|
525
|
+
includeVector: options.includeVector,
|
|
526
|
+
returnMetadata: options.includeMetadata ? ["score", "explainScore"] : undefined,
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// Add BM25-specific options
|
|
530
|
+
if (options.bm25?.properties) {
|
|
531
|
+
queryOptions.queryProperties = options.bm25.properties;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const query = options.bm25?.query || "";
|
|
535
|
+
const result = await collection.query.bm25(query, queryOptions);
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
objects: (result.objects || []).map((obj) => ({
|
|
539
|
+
id: obj.uuid,
|
|
540
|
+
properties: obj.properties as T,
|
|
541
|
+
vector: this.extractVector(obj.vectors),
|
|
542
|
+
certainty: obj.metadata?.certainty,
|
|
543
|
+
distance: obj.metadata?.distance,
|
|
544
|
+
score: obj.metadata?.score,
|
|
545
|
+
metadata: obj.metadata,
|
|
546
|
+
})),
|
|
547
|
+
total: result.objects?.length || 0,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Execute hybrid search
|
|
553
|
+
*/
|
|
554
|
+
private async executeHybridSearch<T>(
|
|
555
|
+
schemaName: string,
|
|
556
|
+
options: HybridSearchOptions,
|
|
557
|
+
): Promise<SearchResult<T>> {
|
|
558
|
+
const client = await this.getClient();
|
|
559
|
+
let collection = client.collections.get(schemaName);
|
|
560
|
+
|
|
561
|
+
// Add tenant context if provided
|
|
562
|
+
if (options.tenant) {
|
|
563
|
+
collection = collection.withTenant(options.tenant.tenant);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Build filter using collection.filter API if plain object provided
|
|
567
|
+
const filters = options.where ? this.buildFilter(collection, options.where) : undefined;
|
|
568
|
+
|
|
569
|
+
const queryOptions: any = {
|
|
570
|
+
limit: options.limit || this.defaultLimit,
|
|
571
|
+
offset: options.offset || 0,
|
|
572
|
+
filters,
|
|
573
|
+
returnProperties: options.fields,
|
|
574
|
+
includeVector: options.includeVector,
|
|
575
|
+
returnMetadata: options.includeMetadata ? ["score", "explainScore"] : undefined,
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// Add hybrid-specific options
|
|
579
|
+
if (options.hybrid?.alpha !== undefined) {
|
|
580
|
+
queryOptions.alpha = options.hybrid.alpha;
|
|
581
|
+
}
|
|
582
|
+
if (options.hybrid?.properties) {
|
|
583
|
+
queryOptions.queryProperties = options.hybrid.properties;
|
|
584
|
+
}
|
|
585
|
+
if (options.hybrid?.vector) {
|
|
586
|
+
queryOptions.vector = options.hybrid.vector;
|
|
587
|
+
}
|
|
588
|
+
if (options.hybrid?.fusionType) {
|
|
589
|
+
queryOptions.fusionType = options.hybrid.fusionType;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const query = options.hybrid?.query || "";
|
|
593
|
+
const result = await collection.query.hybrid(query, queryOptions);
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
objects: (result.objects || []).map((obj) => ({
|
|
597
|
+
id: obj.uuid,
|
|
598
|
+
properties: obj.properties as T,
|
|
599
|
+
vector: this.extractVector(obj.vectors),
|
|
600
|
+
certainty: obj.metadata?.certainty,
|
|
601
|
+
distance: obj.metadata?.distance,
|
|
602
|
+
score: obj.metadata?.score,
|
|
603
|
+
metadata: obj.metadata,
|
|
604
|
+
})),
|
|
605
|
+
total: result.objects?.length || 0,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Extract the first vector from a Weaviate response object's vectors field.
|
|
611
|
+
* Handles both v3 record format ({ default: [...] }) and array format ([[...]])
|
|
612
|
+
*/
|
|
613
|
+
private extractVector(vectors: any): number[] | undefined {
|
|
614
|
+
if (!vectors) return undefined;
|
|
615
|
+
if (Array.isArray(vectors)) return vectors[0] as number[] | undefined;
|
|
616
|
+
const values = Object.values(vectors);
|
|
617
|
+
return values.length > 0 ? (values[0] as number[]) : undefined;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Universal search across multiple schemas
|
|
622
|
+
*
|
|
623
|
+
* @param schemaNames - Array of schema names to search
|
|
624
|
+
* @param request - Search request with method and options
|
|
625
|
+
* @returns Record of schema names to search results
|
|
626
|
+
* @example
|
|
627
|
+
* const results = await weaviate.searchAll(['Person', 'Organization'], {
|
|
628
|
+
* method: 'hybrid',
|
|
629
|
+
* options: { hybrid: { query: 'engineer' }, limit: 10 }
|
|
630
|
+
* });
|
|
631
|
+
*/
|
|
632
|
+
async searchAll<T = any>(
|
|
633
|
+
schemaNames: string[],
|
|
634
|
+
request: SearchRequest,
|
|
635
|
+
): Promise<Record<string, SearchResult<T>>> {
|
|
636
|
+
const results: Record<string, SearchResult<T>> = {};
|
|
637
|
+
|
|
638
|
+
// Execute searches in parallel
|
|
639
|
+
const searchPromises = schemaNames.map(async (schemaName) => {
|
|
640
|
+
try {
|
|
641
|
+
const result = await this.search<T>(schemaName, request);
|
|
642
|
+
return { schemaName, result };
|
|
643
|
+
} catch (error) {
|
|
644
|
+
Logger.log(`Error searching ${schemaName}:`, error);
|
|
645
|
+
return { schemaName, result: { objects: [], total: 0 } as SearchResult<T> };
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
const searchResults = await Promise.all(searchPromises);
|
|
650
|
+
|
|
651
|
+
searchResults.forEach(({ schemaName, result }) => {
|
|
652
|
+
results[schemaName] = result;
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
return results;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Search with automatic method selection based on query
|
|
660
|
+
*
|
|
661
|
+
* @param schemaName - Name of the schema to search
|
|
662
|
+
* @param query - Search query
|
|
663
|
+
* @param options - Additional search options
|
|
664
|
+
* @returns Search results
|
|
665
|
+
* @example
|
|
666
|
+
* const results = await weaviate.smartSearch('Person', 'software engineer');
|
|
667
|
+
*/
|
|
668
|
+
async smartSearch<T = any>(
|
|
669
|
+
schemaName: string,
|
|
670
|
+
query: string,
|
|
671
|
+
options: Omit<SearchOptions, "query"> = {},
|
|
672
|
+
): Promise<SearchResult<T>> {
|
|
673
|
+
// Simple heuristic: if query has quotes or specific terms, use BM25
|
|
674
|
+
// Otherwise use hybrid for best results
|
|
675
|
+
const hasQuotes = query.includes('"') || query.includes("'");
|
|
676
|
+
const hasSpecificTerms = /^[A-Z][a-z]+ [A-Z][a-z]+$/.test(query); // Proper names
|
|
677
|
+
|
|
678
|
+
let method: "vector" | "bm25" | "hybrid";
|
|
679
|
+
if (hasQuotes || hasSpecificTerms) {
|
|
680
|
+
method = "bm25";
|
|
681
|
+
} else if (query.length < 10) {
|
|
682
|
+
method = "vector";
|
|
683
|
+
} else {
|
|
684
|
+
method = "hybrid";
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
Logger.log(`Smart search using ${method} method for query: "${query}"`);
|
|
688
|
+
|
|
689
|
+
// Build proper discriminated union based on selected method
|
|
690
|
+
let request: SearchRequest;
|
|
691
|
+
if (method === "bm25") {
|
|
692
|
+
request = {
|
|
693
|
+
method: "bm25",
|
|
694
|
+
options: { ...options, bm25: { query } },
|
|
695
|
+
};
|
|
696
|
+
} else if (method === "vector") {
|
|
697
|
+
request = {
|
|
698
|
+
method: "vector",
|
|
699
|
+
options: { ...options, nearText: { concepts: [query] } },
|
|
700
|
+
};
|
|
701
|
+
} else {
|
|
702
|
+
request = {
|
|
703
|
+
method: "hybrid",
|
|
704
|
+
options: { ...options, hybrid: { query } },
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return this.search<T>(schemaName, request);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Convenience method for vector search with nearText
|
|
713
|
+
*
|
|
714
|
+
* @param schemaName - Name of the schema to search
|
|
715
|
+
* @param concepts - Concepts to search for (semantic similarity)
|
|
716
|
+
* @param options - Additional search options
|
|
717
|
+
* @returns Search results with matching objects
|
|
718
|
+
* @example
|
|
719
|
+
* const results = await weaviate.vectorSearch('Person', ['software engineer', 'AI expert']);
|
|
720
|
+
*/
|
|
721
|
+
async vectorSearch<T = any>(
|
|
722
|
+
schemaName: string,
|
|
723
|
+
concepts: string[],
|
|
724
|
+
options: Omit<SearchOptions & VectorSearchOptions, "nearText"> = {},
|
|
725
|
+
): Promise<SearchResult<T>> {
|
|
726
|
+
return this.search<T>(schemaName, {
|
|
727
|
+
method: "vector",
|
|
728
|
+
options: {
|
|
729
|
+
...options,
|
|
730
|
+
nearText: { concepts },
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Convenience method for BM25 keyword search
|
|
737
|
+
*
|
|
738
|
+
* @param schemaName - Name of the schema to search
|
|
739
|
+
* @param query - Text query for keyword matching
|
|
740
|
+
* @param options - Additional search options
|
|
741
|
+
* @returns Search results with matching objects
|
|
742
|
+
* @example
|
|
743
|
+
* const results = await weaviate.textSearch('Person', 'John Smith');
|
|
744
|
+
*/
|
|
745
|
+
async textSearch<T = any>(
|
|
746
|
+
schemaName: string,
|
|
747
|
+
query: string,
|
|
748
|
+
options: Omit<SearchOptions & BM25SearchOptions, "bm25"> = {},
|
|
749
|
+
): Promise<SearchResult<T>> {
|
|
750
|
+
return this.search<T>(schemaName, {
|
|
751
|
+
method: "bm25",
|
|
752
|
+
options: {
|
|
753
|
+
...options,
|
|
754
|
+
bm25: { query },
|
|
755
|
+
},
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Convenience method for hybrid search (combines vector + keyword)
|
|
761
|
+
*
|
|
762
|
+
* @param schemaName - Name of the schema to search
|
|
763
|
+
* @param query - Search query
|
|
764
|
+
* @param options - Additional search options (alpha controls the balance)
|
|
765
|
+
* @returns Search results with matching objects
|
|
766
|
+
* @example
|
|
767
|
+
* const results = await weaviate.hybridSearch('Person', 'software engineer');
|
|
768
|
+
*/
|
|
769
|
+
async hybridSearch<T = any>(
|
|
770
|
+
schemaName: string,
|
|
771
|
+
query: string,
|
|
772
|
+
options: Omit<SearchOptions & HybridSearchOptions, "hybrid"> = {},
|
|
773
|
+
): Promise<SearchResult<T>> {
|
|
774
|
+
return this.search<T>(schemaName, {
|
|
775
|
+
method: "hybrid",
|
|
776
|
+
options: {
|
|
777
|
+
...options,
|
|
778
|
+
hybrid: { query },
|
|
779
|
+
},
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Search objects with basic options (defaults to hybrid)
|
|
785
|
+
*
|
|
786
|
+
* @param className - Name of the class to search
|
|
787
|
+
* @param request - Search request with method and options
|
|
788
|
+
* @returns Search results with matching objects
|
|
789
|
+
* @example
|
|
790
|
+
* const results = await weaviate.searchObjects('Person', {
|
|
791
|
+
* method: 'hybrid',
|
|
792
|
+
* options: { hybrid: { query: 'engineer' }, limit: 10 }
|
|
793
|
+
* });
|
|
794
|
+
*/
|
|
795
|
+
async searchObjects<T = any>(
|
|
796
|
+
className: string,
|
|
797
|
+
request: SearchRequest,
|
|
798
|
+
): Promise<SearchResult<T>> {
|
|
799
|
+
return this.search<T>(className, request);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// ==================== CRUD METHODS ====================
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Create a single object in Weaviate
|
|
806
|
+
*
|
|
807
|
+
* @param obj - Object to create with class and properties
|
|
808
|
+
* @returns ID of the created object
|
|
809
|
+
* @example
|
|
810
|
+
* const id = await weaviate.createObject({
|
|
811
|
+
* class: 'Person',
|
|
812
|
+
* properties: { fullName: 'John Doe', email: 'john@example.com' }
|
|
813
|
+
* });
|
|
814
|
+
*/
|
|
815
|
+
async createObject(obj: any): Promise<string> {
|
|
816
|
+
try {
|
|
817
|
+
const client = await this.getClient();
|
|
818
|
+
const collection = client.collections.get(obj.class);
|
|
819
|
+
const result = await collection.data.insert({
|
|
820
|
+
properties: obj.properties,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
return result;
|
|
824
|
+
} catch (error) {
|
|
825
|
+
Logger.log("Error creating object:", error);
|
|
826
|
+
throw WeaviateError.from(
|
|
827
|
+
error,
|
|
828
|
+
WeaviateErrorCode.OBJECT_CREATION_ERROR,
|
|
829
|
+
`Failed to create object in ${obj.class}`,
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Create multiple objects in batch
|
|
836
|
+
*/
|
|
837
|
+
async createObjectsBatch(objects: any[]): Promise<string[]> {
|
|
838
|
+
try {
|
|
839
|
+
const client = await this.getClient();
|
|
840
|
+
|
|
841
|
+
// Group objects by class
|
|
842
|
+
const objectsByClass = objects.reduce(
|
|
843
|
+
(acc, obj) => {
|
|
844
|
+
if (!acc[obj.class]) {
|
|
845
|
+
acc[obj.class] = [];
|
|
846
|
+
}
|
|
847
|
+
acc[obj.class].push(obj.properties);
|
|
848
|
+
return acc;
|
|
849
|
+
},
|
|
850
|
+
{} as Record<string, any[]>,
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
// Insert each class's objects
|
|
854
|
+
const ids: string[] = [];
|
|
855
|
+
for (const [className, props] of Object.entries(objectsByClass)) {
|
|
856
|
+
const collection = client.collections.get(className);
|
|
857
|
+
const result = await collection.data.insertMany(props as any);
|
|
858
|
+
if (result.uuids) {
|
|
859
|
+
ids.push(...Object.values(result.uuids));
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
return ids;
|
|
864
|
+
} catch (error) {
|
|
865
|
+
Logger.log("Error creating objects batch:", error);
|
|
866
|
+
throw WeaviateError.from(
|
|
867
|
+
error,
|
|
868
|
+
WeaviateErrorCode.BATCH_OPERATION_ERROR,
|
|
869
|
+
"Failed to create batch of objects",
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Create an object with a custom vector
|
|
876
|
+
*/
|
|
877
|
+
async createObjectWithVector(
|
|
878
|
+
className: string,
|
|
879
|
+
properties: any,
|
|
880
|
+
vector: number[],
|
|
881
|
+
id?: string,
|
|
882
|
+
tenant?: string,
|
|
883
|
+
): Promise<string> {
|
|
884
|
+
try {
|
|
885
|
+
const client = await this.getClient();
|
|
886
|
+
let collection = client.collections.get(className);
|
|
887
|
+
|
|
888
|
+
// Add tenant context if provided
|
|
889
|
+
if (tenant) {
|
|
890
|
+
collection = collection.withTenant(tenant);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const result = await collection.data.insert({
|
|
894
|
+
properties,
|
|
895
|
+
vectors: vector,
|
|
896
|
+
...(id && { id }),
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
return result;
|
|
900
|
+
} catch (error) {
|
|
901
|
+
Logger.log("Error creating object with vector:", error);
|
|
902
|
+
throw WeaviateError.from(
|
|
903
|
+
error,
|
|
904
|
+
WeaviateErrorCode.OBJECT_CREATION_ERROR,
|
|
905
|
+
`Failed to create object with vector in ${className}`,
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Get an object by ID
|
|
912
|
+
*/
|
|
913
|
+
async getObject<T = any>(
|
|
914
|
+
className: string,
|
|
915
|
+
objectId: string,
|
|
916
|
+
tenant?: string,
|
|
917
|
+
): Promise<T | null> {
|
|
918
|
+
try {
|
|
919
|
+
const client = await this.getClient();
|
|
920
|
+
let collection = client.collections.get(className);
|
|
921
|
+
|
|
922
|
+
// Add tenant context if provided
|
|
923
|
+
if (tenant) {
|
|
924
|
+
collection = collection.withTenant(tenant);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const result = await collection.query.fetchObjectById(objectId);
|
|
928
|
+
|
|
929
|
+
return (result?.properties as T) || null;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
Logger.log("Error getting object:", error);
|
|
932
|
+
throw WeaviateError.from(
|
|
933
|
+
error,
|
|
934
|
+
WeaviateErrorCode.OBJECT_RETRIEVAL_ERROR,
|
|
935
|
+
`Failed to get object ${objectId} from ${className}`,
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Update an object
|
|
942
|
+
*/
|
|
943
|
+
async updateObject(className: string, objectId: string, properties: any): Promise<void> {
|
|
944
|
+
try {
|
|
945
|
+
const client = await this.getClient();
|
|
946
|
+
const collection = client.collections.get(className);
|
|
947
|
+
await collection.data.update({
|
|
948
|
+
id: objectId,
|
|
949
|
+
properties,
|
|
950
|
+
});
|
|
951
|
+
} catch (error) {
|
|
952
|
+
Logger.log("Error updating object:", error);
|
|
953
|
+
throw WeaviateError.from(
|
|
954
|
+
error,
|
|
955
|
+
WeaviateErrorCode.OBJECT_UPDATE_ERROR,
|
|
956
|
+
`Failed to update object ${objectId} in ${className}`,
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Update an object with tenant support (properties only, no vector)
|
|
963
|
+
*/
|
|
964
|
+
async updateObjectWithTenant(
|
|
965
|
+
className: string,
|
|
966
|
+
objectId: string,
|
|
967
|
+
properties: any,
|
|
968
|
+
tenant?: string,
|
|
969
|
+
skipHealthCheck = false,
|
|
970
|
+
): Promise<void> {
|
|
971
|
+
try {
|
|
972
|
+
// Skip health check for batch operations - they handle their own retries
|
|
973
|
+
const client = await this.getClient(skipHealthCheck);
|
|
974
|
+
let collection = client.collections.get(className);
|
|
975
|
+
|
|
976
|
+
// Add tenant context if provided
|
|
977
|
+
if (tenant) {
|
|
978
|
+
collection = collection.withTenant(tenant);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
await collection.data.update({
|
|
982
|
+
id: objectId,
|
|
983
|
+
properties,
|
|
984
|
+
});
|
|
985
|
+
} catch (error) {
|
|
986
|
+
Logger.log("Error updating object with tenant:", error);
|
|
987
|
+
throw WeaviateError.from(
|
|
988
|
+
error,
|
|
989
|
+
WeaviateErrorCode.OBJECT_UPDATE_ERROR,
|
|
990
|
+
`Failed to update object ${objectId} in ${className}`,
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Update an object with a custom vector
|
|
997
|
+
*/
|
|
998
|
+
async updateObjectWithVector(
|
|
999
|
+
className: string,
|
|
1000
|
+
objectId: string,
|
|
1001
|
+
properties: any,
|
|
1002
|
+
vector: number[],
|
|
1003
|
+
tenant?: string,
|
|
1004
|
+
): Promise<void> {
|
|
1005
|
+
try {
|
|
1006
|
+
const client = await this.getClient();
|
|
1007
|
+
let collection = client.collections.get(className);
|
|
1008
|
+
|
|
1009
|
+
// Add tenant context if provided
|
|
1010
|
+
if (tenant) {
|
|
1011
|
+
collection = collection.withTenant(tenant);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
await collection.data.update({
|
|
1015
|
+
id: objectId,
|
|
1016
|
+
properties,
|
|
1017
|
+
vectors: vector,
|
|
1018
|
+
});
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
Logger.log("Error updating object with vector:", error);
|
|
1021
|
+
throw WeaviateError.from(
|
|
1022
|
+
error,
|
|
1023
|
+
WeaviateErrorCode.OBJECT_UPDATE_ERROR,
|
|
1024
|
+
`Failed to update object ${objectId} with vector in ${className}`,
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Delete an object
|
|
1031
|
+
*/
|
|
1032
|
+
async deleteObject(className: string, objectId: string): Promise<void> {
|
|
1033
|
+
try {
|
|
1034
|
+
const client = await this.getClient();
|
|
1035
|
+
const collection = client.collections.get(className);
|
|
1036
|
+
await collection.data.deleteById(objectId);
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
Logger.log("Error deleting object:", error);
|
|
1039
|
+
throw WeaviateError.from(
|
|
1040
|
+
error,
|
|
1041
|
+
WeaviateErrorCode.OBJECT_DELETE_ERROR,
|
|
1042
|
+
`Failed to delete object ${objectId} from ${className}`,
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Get object count for a class
|
|
1049
|
+
*/
|
|
1050
|
+
async getObjectCount(className: string): Promise<number> {
|
|
1051
|
+
try {
|
|
1052
|
+
const client = await this.getClient();
|
|
1053
|
+
const collection = client.collections.get(className);
|
|
1054
|
+
const count = await collection.aggregate.overAll();
|
|
1055
|
+
return count.totalCount || 0;
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
Logger.log("Error getting object count:", error);
|
|
1058
|
+
throw WeaviateError.from(
|
|
1059
|
+
error,
|
|
1060
|
+
WeaviateErrorCode.OBJECT_RETRIEVAL_ERROR,
|
|
1061
|
+
`Failed to get object count for ${className}`,
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// ==================== MULTI-TENANCY METHODS ====================
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Create a new tenant for a multi-tenant collection
|
|
1070
|
+
*
|
|
1071
|
+
* @param className - Name of the multi-tenant class
|
|
1072
|
+
* @param options - Tenant creation options (name, activityStatus)
|
|
1073
|
+
* @returns Result with success status and tenant information
|
|
1074
|
+
* @example
|
|
1075
|
+
* const result = await weaviate.createTenant('UserDocuments', {
|
|
1076
|
+
* name: 'acme-corp',
|
|
1077
|
+
* activityStatus: 'HOT'
|
|
1078
|
+
* });
|
|
1079
|
+
*/
|
|
1080
|
+
async createTenant(
|
|
1081
|
+
className: string,
|
|
1082
|
+
options: CreateTenantOptions,
|
|
1083
|
+
): Promise<TenantManagementResult> {
|
|
1084
|
+
try {
|
|
1085
|
+
// Validate tenant name format
|
|
1086
|
+
if (!TenantContextManager.validateTenantName(options.name)) {
|
|
1087
|
+
const sanitized = TenantContextManager.sanitizeTenantName(options.name);
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
`Invalid tenant name '${options.name}'. Tenant names must be lowercase alphanumeric with hyphens only (max 63 chars). Suggested: '${sanitized}'`,
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const client = await this.getClient();
|
|
1094
|
+
const collection = client.collections.get(className);
|
|
1095
|
+
await collection.tenants.create({
|
|
1096
|
+
name: options.name,
|
|
1097
|
+
activityStatus: options.activityStatus || "HOT",
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
Logger.log(`Tenant '${options.name}' created for class '${className}'`);
|
|
1101
|
+
|
|
1102
|
+
return {
|
|
1103
|
+
success: true,
|
|
1104
|
+
tenant: {
|
|
1105
|
+
name: options.name,
|
|
1106
|
+
activityStatus: options.activityStatus || "HOT",
|
|
1107
|
+
},
|
|
1108
|
+
};
|
|
1109
|
+
} catch (error) {
|
|
1110
|
+
Logger.log(`Error creating tenant '${options.name}':`, error);
|
|
1111
|
+
return {
|
|
1112
|
+
success: false,
|
|
1113
|
+
error: error.message || "Failed to create tenant",
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* List all tenants for a multi-tenant collection
|
|
1120
|
+
*/
|
|
1121
|
+
async listTenants(className: string): Promise<Tenant[]> {
|
|
1122
|
+
try {
|
|
1123
|
+
const client = await this.getClient();
|
|
1124
|
+
const collection = client.collections.get(className);
|
|
1125
|
+
const result = await collection.tenants.get();
|
|
1126
|
+
|
|
1127
|
+
return Object.values(result).map((tenant: any) => ({
|
|
1128
|
+
name: tenant.name,
|
|
1129
|
+
activityStatus: tenant.activityStatus,
|
|
1130
|
+
createdAt: tenant.createdAt,
|
|
1131
|
+
updatedAt: tenant.updatedAt,
|
|
1132
|
+
}));
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
Logger.log(`Error listing tenants for class '${className}':`, error);
|
|
1135
|
+
throw WeaviateError.from(
|
|
1136
|
+
error,
|
|
1137
|
+
WeaviateErrorCode.TENANT_RETRIEVAL_ERROR,
|
|
1138
|
+
`Failed to list tenants for ${className}`,
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Get a specific tenant
|
|
1145
|
+
*/
|
|
1146
|
+
async getTenant(className: string, tenantName: string): Promise<Tenant | null> {
|
|
1147
|
+
try {
|
|
1148
|
+
const tenants = await this.listTenants(className);
|
|
1149
|
+
return tenants.find((tenant) => tenant.name === tenantName) || null;
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
Logger.log(`Error getting tenant '${tenantName}':`, error);
|
|
1152
|
+
throw WeaviateError.from(
|
|
1153
|
+
error,
|
|
1154
|
+
WeaviateErrorCode.TENANT_RETRIEVAL_ERROR,
|
|
1155
|
+
`Failed to get tenant '${tenantName}' from ${className}`,
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Update a tenant
|
|
1162
|
+
*/
|
|
1163
|
+
async updateTenant(
|
|
1164
|
+
className: string,
|
|
1165
|
+
tenantName: string,
|
|
1166
|
+
options: UpdateTenantOptions,
|
|
1167
|
+
): Promise<TenantManagementResult> {
|
|
1168
|
+
try {
|
|
1169
|
+
// Validate tenant name format
|
|
1170
|
+
if (!TenantContextManager.validateTenantName(tenantName)) {
|
|
1171
|
+
throw new Error(
|
|
1172
|
+
`Invalid tenant name '${tenantName}'. Tenant names must be lowercase alphanumeric with hyphens only (max 63 chars).`,
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
const client = await this.getClient();
|
|
1177
|
+
const collection = client.collections.get(className);
|
|
1178
|
+
await collection.tenants.update({
|
|
1179
|
+
name: tenantName,
|
|
1180
|
+
activityStatus: options.activityStatus as "HOT" | "COLD" | "FROZEN" | undefined,
|
|
1181
|
+
});
|
|
1182
|
+
|
|
1183
|
+
Logger.log(`Tenant '${tenantName}' updated for class '${className}'`);
|
|
1184
|
+
|
|
1185
|
+
return {
|
|
1186
|
+
success: true,
|
|
1187
|
+
tenant: {
|
|
1188
|
+
name: tenantName,
|
|
1189
|
+
activityStatus: options.activityStatus || "HOT",
|
|
1190
|
+
},
|
|
1191
|
+
};
|
|
1192
|
+
} catch (error) {
|
|
1193
|
+
Logger.log(`Error updating tenant '${tenantName}':`, error);
|
|
1194
|
+
return {
|
|
1195
|
+
success: false,
|
|
1196
|
+
error: error.message || "Failed to update tenant",
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Delete a tenant
|
|
1203
|
+
*/
|
|
1204
|
+
async deleteTenant(className: string, tenantName: string): Promise<TenantManagementResult> {
|
|
1205
|
+
try {
|
|
1206
|
+
// Validate tenant name format
|
|
1207
|
+
if (!TenantContextManager.validateTenantName(tenantName)) {
|
|
1208
|
+
throw new Error(
|
|
1209
|
+
`Invalid tenant name '${tenantName}'. Tenant names must be lowercase alphanumeric with hyphens only (max 63 chars).`,
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const client = await this.getClient();
|
|
1214
|
+
const collection = client.collections.get(className);
|
|
1215
|
+
await collection.tenants.remove(tenantName);
|
|
1216
|
+
|
|
1217
|
+
Logger.log(`Tenant '${tenantName}' deleted from class '${className}'`);
|
|
1218
|
+
|
|
1219
|
+
return {
|
|
1220
|
+
success: true,
|
|
1221
|
+
};
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
Logger.log(`Error deleting tenant '${tenantName}':`, error);
|
|
1224
|
+
return {
|
|
1225
|
+
success: false,
|
|
1226
|
+
error: error.message || "Failed to delete tenant",
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Get tenant statistics for a collection
|
|
1233
|
+
*/
|
|
1234
|
+
async getTenantStats(className: string): Promise<TenantStats> {
|
|
1235
|
+
try {
|
|
1236
|
+
const tenants = await this.listTenants(className);
|
|
1237
|
+
|
|
1238
|
+
return {
|
|
1239
|
+
totalTenants: tenants.length,
|
|
1240
|
+
activeTenants: tenants.filter((t) => t.activityStatus === "HOT").length,
|
|
1241
|
+
inactiveTenants: tenants.filter((t) => t.activityStatus === "INACTIVE").length,
|
|
1242
|
+
coldTenants: tenants.filter((t) => t.activityStatus === "COLD").length,
|
|
1243
|
+
};
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
Logger.log(`Error getting tenant stats for class '${className}':`, error);
|
|
1246
|
+
throw WeaviateError.from(
|
|
1247
|
+
error,
|
|
1248
|
+
WeaviateErrorCode.TENANT_RETRIEVAL_ERROR,
|
|
1249
|
+
`Failed to get tenant stats for ${className}`,
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* Check if a collection supports multi-tenancy
|
|
1256
|
+
*/
|
|
1257
|
+
async isMultiTenant(className: string): Promise<boolean> {
|
|
1258
|
+
try {
|
|
1259
|
+
const client = await this.getClient();
|
|
1260
|
+
const config = await client.collections.export(className);
|
|
1261
|
+
return config.multiTenancy?.enabled || false;
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
Logger.log(`Error checking multi-tenancy for class '${className}':`, error);
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/**
|
|
1269
|
+
* Search with tenant context
|
|
1270
|
+
*
|
|
1271
|
+
* @param schemaName - Name of the schema to search
|
|
1272
|
+
* @param request - Search request with method and options
|
|
1273
|
+
* @param tenant - Tenant identifier
|
|
1274
|
+
* @returns Search results scoped to the specified tenant
|
|
1275
|
+
* @example
|
|
1276
|
+
* const results = await weaviate.searchWithTenant('Person', {
|
|
1277
|
+
* method: 'hybrid',
|
|
1278
|
+
* options: { hybrid: { query: 'engineer' }, limit: 10 }
|
|
1279
|
+
* }, 'tenant123');
|
|
1280
|
+
*/
|
|
1281
|
+
async searchWithTenant<T = any>(
|
|
1282
|
+
schemaName: string,
|
|
1283
|
+
request: SearchRequest,
|
|
1284
|
+
tenant: string,
|
|
1285
|
+
): Promise<SearchResult<T>> {
|
|
1286
|
+
// Validate tenant name format
|
|
1287
|
+
if (!TenantContextManager.validateTenantName(tenant)) {
|
|
1288
|
+
throw new Error(
|
|
1289
|
+
`Invalid tenant name '${tenant}'. Tenant names must be lowercase alphanumeric with hyphens only (max 63 chars).`,
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// Add tenant to the options
|
|
1294
|
+
const requestWithTenant: SearchRequest = {
|
|
1295
|
+
method: request.method,
|
|
1296
|
+
options: {
|
|
1297
|
+
...request.options,
|
|
1298
|
+
tenant: { tenant },
|
|
1299
|
+
},
|
|
1300
|
+
} as SearchRequest;
|
|
1301
|
+
|
|
1302
|
+
return this.search<T>(schemaName, requestWithTenant);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Smart search with tenant context
|
|
1307
|
+
*/
|
|
1308
|
+
async smartSearchWithTenant<T = any>(
|
|
1309
|
+
schemaName: string,
|
|
1310
|
+
query: string,
|
|
1311
|
+
tenant: string,
|
|
1312
|
+
options: Omit<SearchOptions, "query" | "tenant"> = {},
|
|
1313
|
+
): Promise<SearchResult<T>> {
|
|
1314
|
+
return this.smartSearch<T>(schemaName, query, {
|
|
1315
|
+
...options,
|
|
1316
|
+
tenant: { tenant },
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// ==================== UTILITY METHODS ====================
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Create a query builder for a specific schema
|
|
1324
|
+
*/
|
|
1325
|
+
queryBuilder(schemaName: string): QueryBuilder {
|
|
1326
|
+
return createQueryBuilder(schemaName);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Get query patterns for common operations
|
|
1331
|
+
*/
|
|
1332
|
+
get patterns() {
|
|
1333
|
+
return QueryPatterns;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* Get schema information
|
|
1338
|
+
*/
|
|
1339
|
+
getSchemaInfo(schemaName: string): {
|
|
1340
|
+
schema: SchemaConfig | undefined;
|
|
1341
|
+
searchableFields: string[];
|
|
1342
|
+
filterableFields: string[];
|
|
1343
|
+
sortableFields: string[];
|
|
1344
|
+
plugins: Plugin[];
|
|
1345
|
+
} {
|
|
1346
|
+
return {
|
|
1347
|
+
schema: this.registry.getSchema(schemaName),
|
|
1348
|
+
searchableFields: this.registry.getSearchableFields(schemaName),
|
|
1349
|
+
filterableFields: this.registry.getFilterableFields(schemaName),
|
|
1350
|
+
sortableFields: this.registry.getSortableFields(schemaName),
|
|
1351
|
+
plugins: this.registry.getSchemaPlugins(schemaName),
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* Get service statistics
|
|
1357
|
+
*/
|
|
1358
|
+
getStats(): {
|
|
1359
|
+
registry: ReturnType<SchemaRegistry["getStats"]>;
|
|
1360
|
+
schemas: string[];
|
|
1361
|
+
} {
|
|
1362
|
+
return {
|
|
1363
|
+
registry: this.registry.getStats(),
|
|
1364
|
+
schemas: this.registry.getSchemaNames(),
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* Get the schema registry
|
|
1370
|
+
*/
|
|
1371
|
+
getRegistry(): SchemaRegistry {
|
|
1372
|
+
return this.registry;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Register a custom schema in the registry (without creating in Weaviate)
|
|
1377
|
+
*
|
|
1378
|
+
* @param schema - Schema configuration to register
|
|
1379
|
+
* @example
|
|
1380
|
+
* weaviate.registerSchema(ProductSchema);
|
|
1381
|
+
*/
|
|
1382
|
+
registerSchema(schema: SchemaConfig): void {
|
|
1383
|
+
this.registry.registerSchema(schema);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Register multiple schemas at once
|
|
1388
|
+
*
|
|
1389
|
+
* @param schemas - Record of schema configurations to register
|
|
1390
|
+
*/
|
|
1391
|
+
registerSchemas(schemas: Record<string, SchemaConfig>): void {
|
|
1392
|
+
this.registry.registerSchemas(schemas);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Register a custom plugin for schema operations
|
|
1397
|
+
*
|
|
1398
|
+
* @param plugin - Plugin to register
|
|
1399
|
+
* @example
|
|
1400
|
+
* weaviate.registerPlugin(new MyCustomPlugin());
|
|
1401
|
+
*/
|
|
1402
|
+
registerPlugin(plugin: Plugin): void {
|
|
1403
|
+
this.registry.registerPlugin(plugin);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Health check
|
|
1408
|
+
*/
|
|
1409
|
+
async healthCheck(): Promise<boolean> {
|
|
1410
|
+
try {
|
|
1411
|
+
const client = await this.getClient();
|
|
1412
|
+
await client.getMeta();
|
|
1413
|
+
return true;
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
Logger.log("Weaviate health check failed:", error);
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|