@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,15 @@
|
|
|
1
|
+
import { defineJob, jobRegistry, } from "../jobs/defineJob.js";
|
|
2
|
+
/** Default queue name for cron tasks. */
|
|
3
|
+
export const CRON_QUEUE = "cron";
|
|
4
|
+
export function defineCron(args, register = jobRegistry) {
|
|
5
|
+
return defineJob({
|
|
6
|
+
queue: args.queue ?? CRON_QUEUE,
|
|
7
|
+
name: args.name,
|
|
8
|
+
handler: args.handler,
|
|
9
|
+
options: {
|
|
10
|
+
schedule: { cron: args.schedule, tz: args.tz },
|
|
11
|
+
defaultJobOptions: args.defaultJobOptions,
|
|
12
|
+
},
|
|
13
|
+
}, register);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=defineCron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineCron.js","sourceRoot":"","sources":["../../../../src/app/services/cron/defineCron.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAIT,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAwD9B,yCAAyC;AACzC,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC;AAEjC,MAAM,UAAU,UAAU,CACxB,IAAoB,EACpB,WAA0D,WAAW;IAErE,OAAO,SAAS,CACd;QACE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,UAAU;QAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,OAAO,EAAE;YACP,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;YAC9C,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;SAC1C;KACF,EACD,QAA+B,CAChC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/app/services/cron/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/app/services/cron/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle hooks for `EntityService` / `DrizzleEntityService`.
|
|
3
|
+
*
|
|
4
|
+
* Foundation contract that lets v4.x ship audit-trail, change-data-capture,
|
|
5
|
+
* webhook-fan-out, and tenant-scoping modules without service-by-service surgery.
|
|
6
|
+
*
|
|
7
|
+
* Hooks run inside the same transaction as the operation when one is active.
|
|
8
|
+
* `before*` hooks return either `void` (continue) or a transformed value
|
|
9
|
+
* (mutate the operation). `after*` hooks are observation-only.
|
|
10
|
+
*
|
|
11
|
+
* Compose multiple hook sets via `composeHooks(...)`; subclasses can override
|
|
12
|
+
* `getHooks()` on a service to register service-specific hooks alongside
|
|
13
|
+
* globally-composed ones.
|
|
14
|
+
*
|
|
15
|
+
* The hook context is the canonical framework-wide `RequestContext` (single
|
|
16
|
+
* AsyncLocalStorage instance) — same shape as what the logger and
|
|
17
|
+
* `withTransaction` see. `userId` carries the actor; the full user object is
|
|
18
|
+
* not stored in the context (hooks fetch it from the DB if they need it).
|
|
19
|
+
*/
|
|
20
|
+
import type { RequestContext } from "../../context/RequestContext.js";
|
|
21
|
+
/** Alias kept for ergonomic imports — same shape as `RequestContext`. */
|
|
22
|
+
export type LifecycleHookContext = RequestContext;
|
|
23
|
+
export interface QueryFilter {
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Hook signatures. Each hook is optional; missing hooks are skipped.
|
|
28
|
+
* Generic over the row/insert/update types so subclasses get type safety.
|
|
29
|
+
*/
|
|
30
|
+
export interface LifecycleHooks<TRow = unknown, TInsert = unknown, TPatch = unknown> {
|
|
31
|
+
/** Before an INSERT. Return a transformed `values` to mutate; return void to continue. */
|
|
32
|
+
beforeCreate?(values: TInsert, ctx: LifecycleHookContext): Promise<TInsert | void> | TInsert | void;
|
|
33
|
+
/** After an INSERT. Observation-only. */
|
|
34
|
+
afterCreate?(row: TRow, ctx: LifecycleHookContext): Promise<void> | void;
|
|
35
|
+
/**
|
|
36
|
+
* Before an UPDATE. `prev` is loaded before the write so audit consumers can diff.
|
|
37
|
+
* Return a transformed `patch` to mutate; return void to continue.
|
|
38
|
+
*/
|
|
39
|
+
beforeUpdate?(prev: TRow, patch: TPatch, ctx: LifecycleHookContext): Promise<TPatch | void> | TPatch | void;
|
|
40
|
+
/** After an UPDATE. Observation-only. Receives both prev and next for diffing. */
|
|
41
|
+
afterUpdate?(prev: TRow, next: TRow, ctx: LifecycleHookContext): Promise<void> | void;
|
|
42
|
+
/** Before a DELETE (or soft-delete). Throw to abort the operation. */
|
|
43
|
+
beforeDelete?(row: TRow, ctx: LifecycleHookContext): Promise<void> | void;
|
|
44
|
+
/** After a DELETE / soft-delete. Observation-only. */
|
|
45
|
+
afterDelete?(row: TRow, ctx: LifecycleHookContext): Promise<void> | void;
|
|
46
|
+
/**
|
|
47
|
+
* Before any read query. Return a transformed filter to mutate (e.g., inject
|
|
48
|
+
* `tenantId = currentTenantId` for multi-tenancy). Return void to continue.
|
|
49
|
+
*/
|
|
50
|
+
beforeQuery?(filter: QueryFilter, ctx: LifecycleHookContext): Promise<QueryFilter | void> | QueryFilter | void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compose multiple hook sets into one. Hooks fire in registration order for
|
|
54
|
+
* `before*` (so each can transform the result of the previous) and in reverse
|
|
55
|
+
* order for `after*` (LIFO — useful for nested cross-cutting concerns).
|
|
56
|
+
*
|
|
57
|
+
* Returning `undefined` from a `before*` hook means "no change"; the next hook
|
|
58
|
+
* receives the unchanged value from the previous step.
|
|
59
|
+
*/
|
|
60
|
+
export declare function composeHooks<TRow, TInsert, TPatch>(...sets: Array<LifecycleHooks<TRow, TInsert, TPatch>>): LifecycleHooks<TRow, TInsert, TPatch>;
|
|
61
|
+
//# sourceMappingURL=LifecycleHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LifecycleHooks.d.ts","sourceRoot":"","sources":["../../../../src/app/services/hooks/LifecycleHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,yEAAyE;AACzE,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAElD,MAAM,WAAW,WAAW;IAG1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,MAAM,GAAG,OAAO;IACjF,0FAA0F;IAC1F,YAAY,CAAC,CACX,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;IAE5C,yCAAyC;IACzC,WAAW,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEzE;;;OAGG;IACH,YAAY,CAAC,CACX,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;IAE1C,kFAAkF;IAClF,WAAW,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEtF,sEAAsE;IACtE,YAAY,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE1E,sDAAsD;IACtD,WAAW,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEzE;;;OAGG;IACH,WAAW,CAAC,CACV,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,WAAW,GAAG,IAAI,CAAC;CACrD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAChD,GAAG,IAAI,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,GACpD,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAwDvC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle hooks for `EntityService` / `DrizzleEntityService`.
|
|
3
|
+
*
|
|
4
|
+
* Foundation contract that lets v4.x ship audit-trail, change-data-capture,
|
|
5
|
+
* webhook-fan-out, and tenant-scoping modules without service-by-service surgery.
|
|
6
|
+
*
|
|
7
|
+
* Hooks run inside the same transaction as the operation when one is active.
|
|
8
|
+
* `before*` hooks return either `void` (continue) or a transformed value
|
|
9
|
+
* (mutate the operation). `after*` hooks are observation-only.
|
|
10
|
+
*
|
|
11
|
+
* Compose multiple hook sets via `composeHooks(...)`; subclasses can override
|
|
12
|
+
* `getHooks()` on a service to register service-specific hooks alongside
|
|
13
|
+
* globally-composed ones.
|
|
14
|
+
*
|
|
15
|
+
* The hook context is the canonical framework-wide `RequestContext` (single
|
|
16
|
+
* AsyncLocalStorage instance) — same shape as what the logger and
|
|
17
|
+
* `withTransaction` see. `userId` carries the actor; the full user object is
|
|
18
|
+
* not stored in the context (hooks fetch it from the DB if they need it).
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Compose multiple hook sets into one. Hooks fire in registration order for
|
|
22
|
+
* `before*` (so each can transform the result of the previous) and in reverse
|
|
23
|
+
* order for `after*` (LIFO — useful for nested cross-cutting concerns).
|
|
24
|
+
*
|
|
25
|
+
* Returning `undefined` from a `before*` hook means "no change"; the next hook
|
|
26
|
+
* receives the unchanged value from the previous step.
|
|
27
|
+
*/
|
|
28
|
+
export function composeHooks(...sets) {
|
|
29
|
+
return {
|
|
30
|
+
async beforeCreate(values, ctx) {
|
|
31
|
+
let current = values;
|
|
32
|
+
for (const set of sets) {
|
|
33
|
+
if (!set.beforeCreate)
|
|
34
|
+
continue;
|
|
35
|
+
const r = await set.beforeCreate(current, ctx);
|
|
36
|
+
if (r !== undefined)
|
|
37
|
+
current = r;
|
|
38
|
+
}
|
|
39
|
+
return current;
|
|
40
|
+
},
|
|
41
|
+
async afterCreate(row, ctx) {
|
|
42
|
+
for (let i = sets.length - 1; i >= 0; i--) {
|
|
43
|
+
await sets[i].afterCreate?.(row, ctx);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
async beforeUpdate(prev, patch, ctx) {
|
|
47
|
+
let current = patch;
|
|
48
|
+
for (const set of sets) {
|
|
49
|
+
if (!set.beforeUpdate)
|
|
50
|
+
continue;
|
|
51
|
+
const r = await set.beforeUpdate(prev, current, ctx);
|
|
52
|
+
if (r !== undefined)
|
|
53
|
+
current = r;
|
|
54
|
+
}
|
|
55
|
+
return current;
|
|
56
|
+
},
|
|
57
|
+
async afterUpdate(prev, next, ctx) {
|
|
58
|
+
for (let i = sets.length - 1; i >= 0; i--) {
|
|
59
|
+
await sets[i].afterUpdate?.(prev, next, ctx);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
async beforeDelete(row, ctx) {
|
|
63
|
+
for (const set of sets) {
|
|
64
|
+
await set.beforeDelete?.(row, ctx);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async afterDelete(row, ctx) {
|
|
68
|
+
for (let i = sets.length - 1; i >= 0; i--) {
|
|
69
|
+
await sets[i].afterDelete?.(row, ctx);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
async beforeQuery(filter, ctx) {
|
|
73
|
+
let current = filter;
|
|
74
|
+
for (const set of sets) {
|
|
75
|
+
if (!set.beforeQuery)
|
|
76
|
+
continue;
|
|
77
|
+
const r = await set.beforeQuery(current, ctx);
|
|
78
|
+
if (r !== undefined)
|
|
79
|
+
current = r;
|
|
80
|
+
}
|
|
81
|
+
return current;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=LifecycleHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LifecycleHooks.js","sourceRoot":"","sources":["../../../../src/app/services/hooks/LifecycleHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAwDH;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAG,IAAkD;IAErD,OAAO;QACL,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,GAAG;YAC5B,IAAI,OAAO,GAAG,MAAM,CAAC;YACrB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,YAAY;oBAAE,SAAS;gBAChC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBAC/C,IAAI,CAAC,KAAK,SAAS;oBAAE,OAAO,GAAG,CAAY,CAAC;YAC9C,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG;YACxB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG;YACjC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,YAAY;oBAAE,SAAS;gBAChC,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;gBACrD,IAAI,CAAC,KAAK,SAAS;oBAAE,OAAO,GAAG,CAAW,CAAC;YAC7C,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG;YAC/B,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG;YACzB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG;YACxB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC1C,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG;YAC3B,IAAI,OAAO,GAAG,MAAM,CAAC;YACrB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,WAAW;oBAAE,SAAS;gBAC/B,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBAC9C,IAAI,CAAC,KAAK,SAAS;oBAAE,OAAO,GAAG,CAAgB,CAAC;YAClD,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type ConnectionOptions, type JobsOptions, Queue } from "bullmq";
|
|
2
|
+
import { logger as defaultLogger } from "../../../observability/logger.js";
|
|
3
|
+
import { type JobRegistry } from "./defineJob.js";
|
|
4
|
+
/**
|
|
5
|
+
* `JobsManager` — wires BullMQ Queues + Workers to the framework's
|
|
6
|
+
* declarative job registry.
|
|
7
|
+
*
|
|
8
|
+
* Producer side (always-on): one `Queue` per unique queue name. The
|
|
9
|
+
* framework exposes `enqueue(queue, name, payload, opts?)` for application
|
|
10
|
+
* code to push work.
|
|
11
|
+
*
|
|
12
|
+
* Worker side (opt-in via `WORKER_MODE`): one `Worker` per unique queue
|
|
13
|
+
* name; the worker dispatches incoming jobs to the right handler by
|
|
14
|
+
* `job.name`. Each handler invocation is wrapped in a fresh
|
|
15
|
+
* `RequestContext` so logger / DrizzleEntityService lifecycle hooks see
|
|
16
|
+
* a coherent context (with `requestId` set to the BullMQ job ID).
|
|
17
|
+
*
|
|
18
|
+
* `WORKER_MODE` selects role:
|
|
19
|
+
* - `api` (default in template; no workers spin up — only the producer)
|
|
20
|
+
* - `worker` (workers only — no HTTP server)
|
|
21
|
+
* - `all` (workers + HTTP — local dev default)
|
|
22
|
+
*
|
|
23
|
+
* On `quit()`: closes all queues + workers. Idempotent. Called from the
|
|
24
|
+
* graceful-shutdown manager (which adds it to the SIGTERM hook chain).
|
|
25
|
+
*/
|
|
26
|
+
export type WorkerMode = "api" | "worker" | "all";
|
|
27
|
+
export interface JobsManagerOptions {
|
|
28
|
+
/** ioredis-compatible connection options or a Redis URL. Required by BullMQ. */
|
|
29
|
+
connection: ConnectionOptions;
|
|
30
|
+
/** Mode for this process. Defaults to `process.env.WORKER_MODE` or "api". */
|
|
31
|
+
mode?: WorkerMode;
|
|
32
|
+
/** Override the framework-wide registry. */
|
|
33
|
+
registry?: JobRegistry;
|
|
34
|
+
/** Override the default logger. */
|
|
35
|
+
logger?: typeof defaultLogger;
|
|
36
|
+
}
|
|
37
|
+
export declare class JobsManager {
|
|
38
|
+
private connection;
|
|
39
|
+
private mode;
|
|
40
|
+
private registry;
|
|
41
|
+
private logger;
|
|
42
|
+
private queues;
|
|
43
|
+
private workers;
|
|
44
|
+
private quitting;
|
|
45
|
+
constructor(opts: JobsManagerOptions);
|
|
46
|
+
/** Lazily get / create the BullMQ Queue for a queue name. */
|
|
47
|
+
getQueue(queueName: string): Queue;
|
|
48
|
+
/** Push a job onto its queue. */
|
|
49
|
+
enqueue(queueName: string, jobName: string, payload: unknown, opts?: JobsOptions): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Start workers for every distinct queue (when mode includes worker).
|
|
52
|
+
* Idempotent — calling twice is a no-op. Returns the count of workers
|
|
53
|
+
* started (0 in `api` mode).
|
|
54
|
+
*/
|
|
55
|
+
startWorkers(): Promise<number>;
|
|
56
|
+
/** Graceful shutdown — drain workers, close queues. Idempotent. */
|
|
57
|
+
quit(): Promise<void>;
|
|
58
|
+
getMode(): WorkerMode;
|
|
59
|
+
/**
|
|
60
|
+
* The set of queue names this manager knows about — sourced from the
|
|
61
|
+
* registry (every distinct `queue:` field across `defineJob(...)`
|
|
62
|
+
* declarations). Used by tooling that needs to enumerate queues
|
|
63
|
+
* (Bull-Board admin UI, custom dashboards, debugging endpoints).
|
|
64
|
+
*/
|
|
65
|
+
getQueueNames(): string[];
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=JobsManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobsManager.d.ts","sourceRoot":"","sources":["../../../../src/app/services/jobs/JobsManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,KAAK,EAAU,MAAM,QAAQ,CAAC;AACjF,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAG3E,OAAO,EAAkC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,CAAC;AAElD,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,aAAa,CAAC;CAC/B;AASD,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,MAAM,CAAuB;IAErC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,kBAAkB;IAOpC,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK;IASlC,iCAAiC;IAC3B,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC;IAWhB;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAoErC,mEAAmE;IAC7D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,OAAO,IAAI,UAAU;IAIrB;;;;;OAKG;IACH,aAAa,IAAI,MAAM,EAAE;CAG1B"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Queue, Worker } from "bullmq";
|
|
2
|
+
import { logger as defaultLogger } from "../../../observability/logger.js";
|
|
3
|
+
import { withSpan } from "../../../observability/tracing.js";
|
|
4
|
+
import { runWithRequestContext } from "../../context/RequestContext.js";
|
|
5
|
+
import { jobRegistry as defaultRegistry } from "./defineJob.js";
|
|
6
|
+
function resolveMode(explicit) {
|
|
7
|
+
if (explicit)
|
|
8
|
+
return explicit;
|
|
9
|
+
const envMode = process.env.WORKER_MODE;
|
|
10
|
+
if (envMode === "worker" || envMode === "all" || envMode === "api")
|
|
11
|
+
return envMode;
|
|
12
|
+
return "api";
|
|
13
|
+
}
|
|
14
|
+
export class JobsManager {
|
|
15
|
+
connection;
|
|
16
|
+
mode;
|
|
17
|
+
registry;
|
|
18
|
+
logger;
|
|
19
|
+
queues = new Map();
|
|
20
|
+
workers = [];
|
|
21
|
+
quitting = false;
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
this.connection = opts.connection;
|
|
24
|
+
this.mode = resolveMode(opts.mode);
|
|
25
|
+
this.registry = opts.registry ?? defaultRegistry;
|
|
26
|
+
this.logger = opts.logger ?? defaultLogger;
|
|
27
|
+
}
|
|
28
|
+
/** Lazily get / create the BullMQ Queue for a queue name. */
|
|
29
|
+
getQueue(queueName) {
|
|
30
|
+
let q = this.queues.get(queueName);
|
|
31
|
+
if (!q) {
|
|
32
|
+
q = new Queue(queueName, { connection: this.connection });
|
|
33
|
+
this.queues.set(queueName, q);
|
|
34
|
+
}
|
|
35
|
+
return q;
|
|
36
|
+
}
|
|
37
|
+
/** Push a job onto its queue. */
|
|
38
|
+
async enqueue(queueName, jobName, payload, opts) {
|
|
39
|
+
// Verify the job is registered — surface typos as boot-time errors
|
|
40
|
+
// rather than silent dead-letter behavior.
|
|
41
|
+
const def = this.registry.find(queueName, jobName);
|
|
42
|
+
if (!def) {
|
|
43
|
+
throw new Error(`enqueue(): no job "${jobName}" registered in queue "${queueName}"`);
|
|
44
|
+
}
|
|
45
|
+
const merged = { ...def.options?.defaultJobOptions, ...opts };
|
|
46
|
+
await this.getQueue(queueName).add(jobName, payload, merged);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start workers for every distinct queue (when mode includes worker).
|
|
50
|
+
* Idempotent — calling twice is a no-op. Returns the count of workers
|
|
51
|
+
* started (0 in `api` mode).
|
|
52
|
+
*/
|
|
53
|
+
async startWorkers() {
|
|
54
|
+
if (this.mode === "api")
|
|
55
|
+
return 0;
|
|
56
|
+
if (this.workers.length > 0)
|
|
57
|
+
return this.workers.length;
|
|
58
|
+
for (const queueName of this.registry.queues()) {
|
|
59
|
+
const handlers = this.registry.byQueue(queueName);
|
|
60
|
+
const concurrency = Math.max(...handlers.map((h) => h.options?.concurrency ?? 1));
|
|
61
|
+
const worker = new Worker(queueName, async (job) => {
|
|
62
|
+
const handler = this.registry.find(queueName, job.name);
|
|
63
|
+
if (!handler) {
|
|
64
|
+
this.logger.warn({ queue: queueName, jobName: job.name, jobId: job.id }, "jobs.unknown_job");
|
|
65
|
+
throw new Error(`No handler for job "${job.name}" in queue "${queueName}"`);
|
|
66
|
+
}
|
|
67
|
+
return withSpan(`job.${queueName}.${job.name}`, async () => {
|
|
68
|
+
return runWithRequestContext({ requestId: `job:${job.id ?? "unknown"}` }, async () => {
|
|
69
|
+
return handler.handler(job.data, {
|
|
70
|
+
job,
|
|
71
|
+
context: { requestId: `job:${job.id ?? "unknown"}` },
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}, { "job.queue": queueName, "job.name": job.name, "job.id": job.id ?? "" });
|
|
75
|
+
}, { connection: this.connection, concurrency });
|
|
76
|
+
worker.on("failed", (job, err) => {
|
|
77
|
+
this.logger.error({
|
|
78
|
+
queue: queueName,
|
|
79
|
+
jobName: job?.name,
|
|
80
|
+
jobId: job?.id,
|
|
81
|
+
attemptsMade: job?.attemptsMade,
|
|
82
|
+
err,
|
|
83
|
+
}, "jobs.failed");
|
|
84
|
+
});
|
|
85
|
+
this.workers.push(worker);
|
|
86
|
+
}
|
|
87
|
+
// Register repeatable jobs
|
|
88
|
+
for (const def of this.registry.list()) {
|
|
89
|
+
if (def.options?.schedule) {
|
|
90
|
+
await this.getQueue(def.queue).add(def.name, undefined, {
|
|
91
|
+
repeat: { pattern: def.options.schedule.cron, tz: def.options.schedule.tz },
|
|
92
|
+
...def.options.defaultJobOptions,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return this.workers.length;
|
|
97
|
+
}
|
|
98
|
+
/** Graceful shutdown — drain workers, close queues. Idempotent. */
|
|
99
|
+
async quit() {
|
|
100
|
+
if (this.quitting)
|
|
101
|
+
return;
|
|
102
|
+
this.quitting = true;
|
|
103
|
+
await Promise.all(this.workers.map((w) => w.close().catch(() => undefined)));
|
|
104
|
+
await Promise.all([...this.queues.values()].map((q) => q.close().catch(() => undefined)));
|
|
105
|
+
this.workers = [];
|
|
106
|
+
this.queues.clear();
|
|
107
|
+
}
|
|
108
|
+
getMode() {
|
|
109
|
+
return this.mode;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The set of queue names this manager knows about — sourced from the
|
|
113
|
+
* registry (every distinct `queue:` field across `defineJob(...)`
|
|
114
|
+
* declarations). Used by tooling that needs to enumerate queues
|
|
115
|
+
* (Bull-Board admin UI, custom dashboards, debugging endpoints).
|
|
116
|
+
*/
|
|
117
|
+
getQueueNames() {
|
|
118
|
+
return this.registry.queues();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=JobsManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobsManager.js","sourceRoot":"","sources":["../../../../src/app/services/jobs/JobsManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bull-Board admin UI integration — wraps `@bull-board/hono` with API-key
|
|
3
|
+
* auth and auto-discovers queues from the framework's `JobsManager`.
|
|
4
|
+
*
|
|
5
|
+
* **Optional integration.** The `@bull-board/hono` and `@bull-board/api`
|
|
6
|
+
* packages are NOT in the framework's `dependencies` — consumers who
|
|
7
|
+
* want the dashboard install them in their own app. Importing this
|
|
8
|
+
* module without those packages installed throws `MODULE_NOT_FOUND` at
|
|
9
|
+
* import time, which is intentional: the framework doesn't pay for an
|
|
10
|
+
* EJS-based admin UI it doesn't always need.
|
|
11
|
+
*
|
|
12
|
+
* Why API-key auth instead of `requireAuth`:
|
|
13
|
+
* - Bull-Board is a *static* UI bundle plus a single API surface; the
|
|
14
|
+
* framework's per-route auth chain isn't a clean fit (Bull-Board
|
|
15
|
+
* registers its own routes inside `serverAdapter.registerPlugin()`).
|
|
16
|
+
* - Operators typically guard the dashboard at the network edge
|
|
17
|
+
* (private subnet, VPN, ingress allowlist) AND at the application
|
|
18
|
+
* layer (this API key) — defense in depth. Don't expose this on a
|
|
19
|
+
* public hostname even with the key set.
|
|
20
|
+
* - The `x-admin-api-key` header is constant-time-compared against
|
|
21
|
+
* the configured secret to avoid timing oracles.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { createBullBoardApp } from "@sixthsummit/ship-api/app/services/jobs/bullBoard";
|
|
26
|
+
*
|
|
27
|
+
* const bb = createBullBoardApp({
|
|
28
|
+
* jobsManager,
|
|
29
|
+
* apiKey: env.ADMIN_API_KEY, // required, ≥32 chars recommended
|
|
30
|
+
* basePath: "/admin/jobs", // optional, default "/admin/jobs"
|
|
31
|
+
* });
|
|
32
|
+
* app.route(bb.basePath, bb.app);
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* The dashboard auto-refreshes its queue list — but it's a *snapshot*
|
|
36
|
+
* at adapter-construction time. If `defineJob(...)` declarations change
|
|
37
|
+
* after the dashboard is mounted (e.g. dynamic module loading), call
|
|
38
|
+
* `bb.refresh()` to re-derive the queue list.
|
|
39
|
+
*/
|
|
40
|
+
import type { Hono } from "hono";
|
|
41
|
+
import type { JobsManager } from "./JobsManager.js";
|
|
42
|
+
export interface BullBoardOptions {
|
|
43
|
+
jobsManager: JobsManager;
|
|
44
|
+
/**
|
|
45
|
+
* API key required on the `x-admin-api-key` header. The framework
|
|
46
|
+
* enforces this is non-empty; production deployments should source it
|
|
47
|
+
* from a secret manager and rotate it independently of JWT keys.
|
|
48
|
+
*/
|
|
49
|
+
apiKey: string;
|
|
50
|
+
/**
|
|
51
|
+
* Mount path. Defaults to `/admin/jobs`. The consumer must mount the
|
|
52
|
+
* returned `app` at the same path on the parent Hono — the value
|
|
53
|
+
* passed here is forwarded to Bull-Board's `setBasePath` so the
|
|
54
|
+
* dashboard's static-asset URLs resolve correctly.
|
|
55
|
+
*/
|
|
56
|
+
basePath?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface BullBoardHandle {
|
|
59
|
+
/** The Hono sub-app to `parent.route(basePath, ...)` onto your app. */
|
|
60
|
+
app: Hono;
|
|
61
|
+
/** The configured (or default) base path. */
|
|
62
|
+
basePath: string;
|
|
63
|
+
/** Re-derive the queue list from the JobsManager registry. */
|
|
64
|
+
refresh: () => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the Bull-Board sub-app. Throws if `apiKey` is empty. Throws
|
|
68
|
+
* `MODULE_NOT_FOUND` if `@bull-board/hono` / `@bull-board/api` are not
|
|
69
|
+
* installed (framework treats them as optional integrations).
|
|
70
|
+
*/
|
|
71
|
+
export declare function createBullBoardApp(opts: BullBoardOptions): Promise<BullBoardHandle>;
|
|
72
|
+
//# sourceMappingURL=bullBoard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullBoard.d.ts","sourceRoot":"","sources":["../../../../src/app/services/jobs/bullBoard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAQH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,GAAG,EAAE,IAAI,CAAC;IACV,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAkEzF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bull-Board admin UI integration — wraps `@bull-board/hono` with API-key
|
|
3
|
+
* auth and auto-discovers queues from the framework's `JobsManager`.
|
|
4
|
+
*
|
|
5
|
+
* **Optional integration.** The `@bull-board/hono` and `@bull-board/api`
|
|
6
|
+
* packages are NOT in the framework's `dependencies` — consumers who
|
|
7
|
+
* want the dashboard install them in their own app. Importing this
|
|
8
|
+
* module without those packages installed throws `MODULE_NOT_FOUND` at
|
|
9
|
+
* import time, which is intentional: the framework doesn't pay for an
|
|
10
|
+
* EJS-based admin UI it doesn't always need.
|
|
11
|
+
*
|
|
12
|
+
* Why API-key auth instead of `requireAuth`:
|
|
13
|
+
* - Bull-Board is a *static* UI bundle plus a single API surface; the
|
|
14
|
+
* framework's per-route auth chain isn't a clean fit (Bull-Board
|
|
15
|
+
* registers its own routes inside `serverAdapter.registerPlugin()`).
|
|
16
|
+
* - Operators typically guard the dashboard at the network edge
|
|
17
|
+
* (private subnet, VPN, ingress allowlist) AND at the application
|
|
18
|
+
* layer (this API key) — defense in depth. Don't expose this on a
|
|
19
|
+
* public hostname even with the key set.
|
|
20
|
+
* - The `x-admin-api-key` header is constant-time-compared against
|
|
21
|
+
* the configured secret to avoid timing oracles.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { createBullBoardApp } from "@sixthsummit/ship-api/app/services/jobs/bullBoard";
|
|
26
|
+
*
|
|
27
|
+
* const bb = createBullBoardApp({
|
|
28
|
+
* jobsManager,
|
|
29
|
+
* apiKey: env.ADMIN_API_KEY, // required, ≥32 chars recommended
|
|
30
|
+
* basePath: "/admin/jobs", // optional, default "/admin/jobs"
|
|
31
|
+
* });
|
|
32
|
+
* app.route(bb.basePath, bb.app);
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* The dashboard auto-refreshes its queue list — but it's a *snapshot*
|
|
36
|
+
* at adapter-construction time. If `defineJob(...)` declarations change
|
|
37
|
+
* after the dashboard is mounted (e.g. dynamic module loading), call
|
|
38
|
+
* `bb.refresh()` to re-derive the queue list.
|
|
39
|
+
*/
|
|
40
|
+
import { timingSafeEqual } from "node:crypto";
|
|
41
|
+
/**
|
|
42
|
+
* Build the Bull-Board sub-app. Throws if `apiKey` is empty. Throws
|
|
43
|
+
* `MODULE_NOT_FOUND` if `@bull-board/hono` / `@bull-board/api` are not
|
|
44
|
+
* installed (framework treats them as optional integrations).
|
|
45
|
+
*/
|
|
46
|
+
export async function createBullBoardApp(opts) {
|
|
47
|
+
if (!opts.apiKey || opts.apiKey.length === 0) {
|
|
48
|
+
throw new Error("createBullBoardApp: apiKey is required (set ADMIN_API_KEY in env)");
|
|
49
|
+
}
|
|
50
|
+
const basePath = opts.basePath ?? "/admin/jobs";
|
|
51
|
+
// Dynamic imports — keep these out of the framework's dep tree so the
|
|
52
|
+
// ~30 KB EJS + UI bundle doesn't ship with every consumer.
|
|
53
|
+
const [{ HonoAdapter }, { createBullBoard }, { BullMQAdapter }, { serveStatic }, { Hono: HonoCtor },] = await Promise.all([
|
|
54
|
+
import("@bull-board/hono"),
|
|
55
|
+
import("@bull-board/api"),
|
|
56
|
+
import("@bull-board/api/bullMQAdapter"),
|
|
57
|
+
import("@hono/node-server/serve-static"),
|
|
58
|
+
import("hono"),
|
|
59
|
+
]);
|
|
60
|
+
const serverAdapter = new HonoAdapter(serveStatic);
|
|
61
|
+
serverAdapter.setBasePath(basePath);
|
|
62
|
+
const buildAdapters = () => opts.jobsManager
|
|
63
|
+
.getQueueNames()
|
|
64
|
+
.map((name) => new BullMQAdapter(opts.jobsManager.getQueue(name)));
|
|
65
|
+
const board = createBullBoard({
|
|
66
|
+
queues: buildAdapters(),
|
|
67
|
+
serverAdapter,
|
|
68
|
+
});
|
|
69
|
+
const apiKeyBytes = Buffer.from(opts.apiKey);
|
|
70
|
+
const app = new HonoCtor();
|
|
71
|
+
app.use("*", async (c, next) => {
|
|
72
|
+
const supplied = c.req.header("x-admin-api-key");
|
|
73
|
+
if (!supplied) {
|
|
74
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
75
|
+
}
|
|
76
|
+
const suppliedBytes = Buffer.from(supplied);
|
|
77
|
+
// Constant-time compare. Length check first because `timingSafeEqual`
|
|
78
|
+
// throws on mismatched buffer lengths — but the early-return on a
|
|
79
|
+
// length mismatch is itself a small timing channel. The keyspace is
|
|
80
|
+
// operator-controlled, so this is acceptable.
|
|
81
|
+
if (suppliedBytes.length !== apiKeyBytes.length ||
|
|
82
|
+
!timingSafeEqual(suppliedBytes, apiKeyBytes)) {
|
|
83
|
+
return c.json({ error: "Unauthorized" }, 401);
|
|
84
|
+
}
|
|
85
|
+
return next();
|
|
86
|
+
});
|
|
87
|
+
app.route("/", serverAdapter.registerPlugin());
|
|
88
|
+
return {
|
|
89
|
+
app,
|
|
90
|
+
basePath,
|
|
91
|
+
refresh: () => {
|
|
92
|
+
board.replaceQueues(buildAdapters());
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=bullBoard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bullBoard.js","sourceRoot":"","sources":["../../../../src/app/services/jobs/bullBoard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAmC9C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAsB;IAC7D,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC;IAEhD,sEAAsE;IACtE,2DAA2D;IAC3D,MAAM,CACJ,EAAE,WAAW,EAAE,EACf,EAAE,eAAe,EAAE,EACnB,EAAE,aAAa,EAAE,EACjB,EAAE,WAAW,EAAE,EACf,EAAE,IAAI,EAAE,QAAQ,EAAE,EACnB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpB,MAAM,CAAC,kBAAkB,CAAC;QAC1B,MAAM,CAAC,iBAAiB,CAAC;QACzB,MAAM,CAAC,+BAA+B,CAAC;QACvC,MAAM,CAAC,gCAAgC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC;KACf,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC;IACnD,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEpC,MAAM,aAAa,GAAG,GAAG,EAAE,CACzB,IAAI,CAAC,WAAW;SACb,aAAa,EAAE;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEvE,MAAM,KAAK,GAAG,eAAe,CAAC;QAC5B,MAAM,EAAE,aAAa,EAAE;QACvB,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE7C,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAU,CAAC;IACnC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QAC7B,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;QACD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5C,sEAAsE;QACtE,kEAAkE;QAClE,oEAAoE;QACpE,8CAA8C;QAC9C,IACE,aAAa,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM;YAC3C,CAAC,eAAe,CAAC,aAAa,EAAE,WAAW,CAAC,EAC5C,CAAC;YACD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,cAAc,EAAE,CAAC,CAAC;IAE/C,OAAO;QACL,GAAG;QACH,QAAQ;QACR,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,CAAC,aAAa,CAAC,aAAa,EAAE,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;AACJ,CAAC"}
|