@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,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "Conventional Commits for consumer apps"
|
|
4
|
+
tags: ["commits", "git", "conventional-commits"]
|
|
5
|
+
globs: ["**/*"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Commit Conventions
|
|
9
|
+
|
|
10
|
+
This project uses [Conventional Commits](https://www.conventionalcommits.org/) for clarity in review and history. If you wire in a release automation later (Changesets, release-please, semantic-release), it will read these prefixes — but the convention is useful on its own even without one.
|
|
11
|
+
|
|
12
|
+
## Format
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
<type>(<scope>): <short description>
|
|
16
|
+
|
|
17
|
+
<optional body — explain WHY, not WHAT>
|
|
18
|
+
|
|
19
|
+
<optional footer — BREAKING CHANGE: ..., refs: ...>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Types
|
|
23
|
+
|
|
24
|
+
| Type | Use for |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `feat` | new user-facing feature |
|
|
27
|
+
| `fix` | bug fix |
|
|
28
|
+
| `feat!` / `BREAKING CHANGE:` footer | breaking change |
|
|
29
|
+
| `docs` | documentation only |
|
|
30
|
+
| `style` | whitespace, formatting (no behavior change) |
|
|
31
|
+
| `refactor` | code change without behavior change |
|
|
32
|
+
| `perf` | performance fix |
|
|
33
|
+
| `test` | test additions / changes |
|
|
34
|
+
| `build` | build system, deps |
|
|
35
|
+
| `ci` | CI / workflow / pipeline changes |
|
|
36
|
+
| `chore` | tooling, internal cleanup |
|
|
37
|
+
| `revert` | reverting a prior commit |
|
|
38
|
+
|
|
39
|
+
If you adopt a release automation that reads commit types (e.g. release-please), `feat:` → minor, `fix:`/`perf:` → patch, `feat!:` → major. Otherwise these are descriptive only.
|
|
40
|
+
|
|
41
|
+
## Scope
|
|
42
|
+
|
|
43
|
+
Optional. Use the area of the change. Common scopes for ShipAPI apps:
|
|
44
|
+
|
|
45
|
+
- `(api)` / `(routes)` / `(modules)` — module surface changes
|
|
46
|
+
- `(db)` — schema, migrations, queries
|
|
47
|
+
- `(auth)` — authentication or permissions
|
|
48
|
+
- `(jobs)` — BullMQ jobs / cron
|
|
49
|
+
- `(test)` — when type is `chore` / `refactor` and only tests change
|
|
50
|
+
|
|
51
|
+
Skip the scope when it's not informative — a one-word commit needs no scope.
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
feat(api): add POST /posts/:id/publish
|
|
57
|
+
|
|
58
|
+
Endpoint already gates on posts:publish permission and emits a
|
|
59
|
+
post.published outbox event for the notifications worker.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
fix(auth): refresh token rotation no longer races on logout
|
|
64
|
+
|
|
65
|
+
The refresh table delete and new-token insert ran in separate
|
|
66
|
+
transactions; concurrent logout + refresh could double-spend a
|
|
67
|
+
token. Wrap both in withTransaction.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
ci: skip e2e suite on docs-only PRs
|
|
72
|
+
|
|
73
|
+
paths-ignore was matching .md files but not the docs/ tree.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
feat!(api): drop /v1/* routes; clients must move to /v2/*
|
|
78
|
+
|
|
79
|
+
BREAKING CHANGE: Endpoints under /v1/ have been removed. See
|
|
80
|
+
docs/MIGRATION_V1_TO_V2.md for the field-by-field map.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Don't
|
|
84
|
+
|
|
85
|
+
- Don't use `git commit --amend` after pushing — it rewrites history and breaks anyone who pulled.
|
|
86
|
+
- Don't `--no-verify`. If a hook fails, fix the underlying issue. Bypassing hooks is how secrets get committed.
|
|
87
|
+
- Don't write multi-paragraph commit bodies for trivial changes. The body should add information the diff doesn't.
|
|
88
|
+
- Don't reference internal ticket IDs in the title — the body is fine, but titles get rendered in CHANGELOG.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "Security guidelines for ShipAPI v4 apps"
|
|
4
|
+
tags: ["security", "authentication", "authorization"]
|
|
5
|
+
globs: ["modules/**/*.ts", "src/index.ts", "src/environment.ts"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Security Guidelines
|
|
9
|
+
|
|
10
|
+
## Authentication
|
|
11
|
+
|
|
12
|
+
ShipAPI v4 uses a pluggable `AuthProvider` interface. The default adapter is `FirebaseAuthProvider`; v4.x will add Better-Auth / Clerk / WorkOS / Auth.js adapters.
|
|
13
|
+
|
|
14
|
+
### Token verification
|
|
15
|
+
|
|
16
|
+
- Bearer tokens (Authorization header or session cookie) are verified by the configured `AuthProvider`.
|
|
17
|
+
- The framework's `JwtAuthFlow` issues stateless ES256 JWT cookies (`httpOnly, secure, sameSite=lax`) plus a `refresh_tokens` table for revocation.
|
|
18
|
+
- **Never disable signature verification.** Don't bypass the provider chain "for tests" — use a `MockAuthProvider` adapter instead.
|
|
19
|
+
|
|
20
|
+
### Middleware
|
|
21
|
+
|
|
22
|
+
- `requireAuth({ provider })` — rejects with 401 if no valid token. Set `optional: true` only for routes that have a public + authenticated mode.
|
|
23
|
+
- `requirePermission(slug)` — rejects with 403 if the resolved account lacks the slug.
|
|
24
|
+
- **Authorization runs at the route, not the controller.** Always chain `requireAuth → requirePermission` on protected routes. The framework's `ship-reviewer` plugin agent flags missing chains.
|
|
25
|
+
|
|
26
|
+
### Permission slugs
|
|
27
|
+
|
|
28
|
+
- Format: `module:resource:action` (e.g., `posts:create`, `accounts:list`, `billing:invoice:read`).
|
|
29
|
+
- Define as constants in `modules/<name>/permissions.ts`. Reference the constant from both `requirePermission(slug)` and any seed-data inserts.
|
|
30
|
+
- Permissions are durable — adding new ones is fine, renaming or removing one needs a migration plan.
|
|
31
|
+
|
|
32
|
+
### Secrets
|
|
33
|
+
|
|
34
|
+
- **No hardcoded fallbacks** in `src/environment.ts`. `defineEnv(zodSchema)` fail-fasts at boot — that's the design. A boot failure is preferable to running on an attacker-known default.
|
|
35
|
+
- Never commit `.env` files. The template's `.env.example` is the only env file checked in.
|
|
36
|
+
- Rotate `JWT_SIGNING_KEY` per the framework's documented procedure (add new key to `JWT_VERIFICATION_KEYS` first, cut signing over, wait > max token lifetime, remove old key).
|
|
37
|
+
|
|
38
|
+
## Input validation
|
|
39
|
+
|
|
40
|
+
- All request input goes through Zod via `validate("json"|"query"|"param", schema)`. Read with `c.req.valid(...)`.
|
|
41
|
+
- Use `.strict()` on object schemas to reject unknown fields — defense against parameter-injection accidents and forward-compat bugs.
|
|
42
|
+
- Validate at the boundary; don't re-validate inside services.
|
|
43
|
+
|
|
44
|
+
### Webhooks
|
|
45
|
+
|
|
46
|
+
- Webhook routes (`POST /webhooks/<provider>`) need raw-body signature verification. Use `parseWebhook(c, { provider, secret })` — handles Stripe, GitHub, Slack, Twilio out of the box; pluggable via the `WebhookVerifier` interface for custom providers.
|
|
47
|
+
- Don't apply `validate("json", ...)` before signature verification — Zod consuming the body breaks the raw-body contract some signatures depend on.
|
|
48
|
+
|
|
49
|
+
### File uploads
|
|
50
|
+
|
|
51
|
+
- Use `parseUpload(c, schema)` from the framework — Web Standards FormData with magic-byte MIME validation via `file-type`.
|
|
52
|
+
- Be aware that uploads are buffered in memory up to `bodyLimit` (default 25 MB) before the handler runs. For large files, drop to `c.req.raw.body` and pipe directly to a `StorageService` adapter.
|
|
53
|
+
- Always validate MIME via magic bytes, not the Content-Type header (which clients control).
|
|
54
|
+
|
|
55
|
+
## CSRF
|
|
56
|
+
|
|
57
|
+
- **Default**: `httpOnly, secure, sameSite=lax` cookies + automatic Origin / Sec-Fetch-Site checking on mutating requests. Protects against top-level navigation CSRF.
|
|
58
|
+
- **Hardened mode**: `csrf({ doubleSubmit: true })` middleware adds an `X-CSRF-Token` double-submit cookie. Opt in for third-party-iframed apps or multi-subdomain consumers.
|
|
59
|
+
|
|
60
|
+
## Rate limit
|
|
61
|
+
|
|
62
|
+
- `rateLimit({ windowMs, max, keyGenerator, scope })` — Redis-backed sliding window. Apply to login / signup / password-reset / any endpoint where unauthenticated retry is cheap.
|
|
63
|
+
- **Default fail-mode is fail-closed** when Redis is unavailable. Override with `onRedisUnavailable: 'allow'` only with a deliberate threat-model decision.
|
|
64
|
+
- Emits RFC-draft `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Policy` headers.
|
|
65
|
+
|
|
66
|
+
## SQL injection
|
|
67
|
+
|
|
68
|
+
- Drizzle uses parameterized queries by default — `eq(table.col, userInput)` is safe.
|
|
69
|
+
- Don't drop to raw SQL via `sql\`...\${input}...\`` with user input interpolated. If you need raw SQL, parameterize via `sql.placeholder` or build the string from a constant whitelist.
|
|
70
|
+
|
|
71
|
+
## Logging
|
|
72
|
+
|
|
73
|
+
- Sensitive fields are redacted by pino's redaction list: `authorization`, `cookie`, `*.password`, `*.token`, `firebaseCredentials`.
|
|
74
|
+
- **Never `console.log` user PII or secrets.** `console.*` bypasses redaction; use `logger`.
|
|
75
|
+
- Don't log full request bodies on routes that accept secrets (login, password reset, payment intent).
|
|
76
|
+
|
|
77
|
+
## WebSocket auth
|
|
78
|
+
|
|
79
|
+
- The framework uses subprotocol-header authentication: client opens `new WebSocket(url, ['ship-api.v1', \`bearer.${token}\`])`. Server reads `Sec-WebSocket-Protocol`, runs the same `AuthProvider` chain as `requireAuth`, rejects the upgrade with 401 if invalid.
|
|
80
|
+
- Don't auth via URL `?token=...` — leaks into access logs. Don't auth via first-message handshake — creates "connected but unauthenticated" footgun.
|
|
81
|
+
|
|
82
|
+
## Audit
|
|
83
|
+
|
|
84
|
+
- `audit-ci` runs in CI and hard-fails on `high` / `critical` findings. Don't `--allow-list` a high finding without a tracking issue + remediation date.
|
|
85
|
+
- Run `npm run audit` locally before opening a PR that bumps deps.
|
|
86
|
+
|
|
87
|
+
## When in doubt
|
|
88
|
+
|
|
89
|
+
- Default to fail-closed.
|
|
90
|
+
- Default to validating at boundaries.
|
|
91
|
+
- Default to using the framework's middleware factories rather than rolling your own.
|
|
92
|
+
- If a security-relevant change feels surprising, check `docs/V4_LAUNCH.md` and the framework's `.cursor/rules/security.mdc` for the intended pattern.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "Vitest testing patterns for ShipAPI v4 apps"
|
|
4
|
+
tags: ["testing", "vitest"]
|
|
5
|
+
globs: ["src/**/*.test.ts", "tests/**/*.ts"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Testing Guidelines
|
|
9
|
+
|
|
10
|
+
## Framework
|
|
11
|
+
|
|
12
|
+
[Vitest](https://vitest.dev). The template ships with `vitest` configured; run with `npm test`.
|
|
13
|
+
|
|
14
|
+
- `npm test` — full run.
|
|
15
|
+
- `npm test -- --watch` — watch mode for active development.
|
|
16
|
+
- `npm test -- --changed` — only re-run tests whose source-of-truth changed since `main`.
|
|
17
|
+
- `npm test -- modules/<name>` — module-scoped run.
|
|
18
|
+
|
|
19
|
+
## Test file co-location
|
|
20
|
+
|
|
21
|
+
Co-locate unit tests with the code under test:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
modules/posts/
|
|
25
|
+
├── services/PostService.ts
|
|
26
|
+
├── services/PostService.test.ts # service unit tests
|
|
27
|
+
├── routes.ts
|
|
28
|
+
└── routes.test.ts # route integration tests
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Integration tests that span modules live under `tests/integration/`.
|
|
32
|
+
|
|
33
|
+
## Service unit tests (fake DB)
|
|
34
|
+
|
|
35
|
+
For service-level tests, mock the DB at the lowest level — you're testing service logic, not Drizzle:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { describe, it, expect, vi } from "vitest";
|
|
39
|
+
import { PostService } from "./PostService";
|
|
40
|
+
|
|
41
|
+
describe("PostService", () => {
|
|
42
|
+
it("filters out soft-deleted rows by default", async () => {
|
|
43
|
+
const fakeDb = { query: { posts: { findMany: vi.fn().mockResolvedValue([]) } } };
|
|
44
|
+
const service = new PostService(fakeDb as any);
|
|
45
|
+
await service.list({});
|
|
46
|
+
expect(fakeDb.query.posts.findMany).toHaveBeenCalledWith(
|
|
47
|
+
expect.objectContaining({ where: expect.anything() })
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For multi-method flows, use `testcontainers` against a real Postgres (see integration tests below).
|
|
54
|
+
|
|
55
|
+
## Route integration tests (full chain)
|
|
56
|
+
|
|
57
|
+
Test the full middleware chain (auth → permission → validate → handler → service) via `app.request(...)` against the chained Hono app. The framework's reference modules ship a harness — mirror that shape:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { describe, it, expect } from "vitest";
|
|
61
|
+
import { app } from "../../index"; // your composed app
|
|
62
|
+
|
|
63
|
+
describe("POST /posts", () => {
|
|
64
|
+
it("rejects unauthenticated requests", async () => {
|
|
65
|
+
const res = await app.request("/posts", {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "content-type": "application/json" },
|
|
68
|
+
body: JSON.stringify({ title: "x", body: "y" }),
|
|
69
|
+
});
|
|
70
|
+
expect(res.status).toBe(401);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("creates a post for an authenticated user with permission", async () => {
|
|
74
|
+
const res = await app.request("/posts", {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "content-type": "application/json", cookie: signedAuthCookie() },
|
|
77
|
+
body: JSON.stringify({ title: "x", body: "y" }),
|
|
78
|
+
});
|
|
79
|
+
expect(res.status).toBe(201);
|
|
80
|
+
const json = await res.json();
|
|
81
|
+
expect(json).toMatchObject({ id: expect.any(String), title: "x" });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`signedAuthCookie()` is a test helper you write once that mints a valid session cookie via the framework's `JwtSigner`. Keep helpers in `tests/helpers/`.
|
|
87
|
+
|
|
88
|
+
## Real-DB integration tests
|
|
89
|
+
|
|
90
|
+
For tests that exercise queries / migrations / transactions against a real Postgres, use `@testcontainers/postgresql`:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { PostgreSqlContainer } from "@testcontainers/postgresql";
|
|
94
|
+
|
|
95
|
+
let container: StartedPostgreSqlContainer;
|
|
96
|
+
beforeAll(async () => { container = await new PostgreSqlContainer().start(); });
|
|
97
|
+
afterAll(async () => { await container.stop(); });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Run integration tests under a separate Vitest config (`vitest.integration.config.ts`) so they don't slow the regular suite.
|
|
101
|
+
|
|
102
|
+
## Mocking
|
|
103
|
+
|
|
104
|
+
- **Mock at boundaries, not internals.** Mock the framework's `AuthProvider`, `Cache`, `JobQueue` — not your own service methods. If you find yourself mocking your own service heavily, the test is at the wrong level.
|
|
105
|
+
- Use `vi.fn()` over `jest.fn()` (the migration is mechanical; vitest exposes `vi`).
|
|
106
|
+
- Use `vi.spyOn(obj, "method").mockReturnValue(...)` for method spies — don't reassign methods directly.
|
|
107
|
+
|
|
108
|
+
## What to assert
|
|
109
|
+
|
|
110
|
+
- **Status codes + response shape**, not exact response bodies. Use `toMatchObject` over `toEqual` so non-deterministic fields (timestamps, IDs) don't make tests brittle.
|
|
111
|
+
- **Side effects**: did the service call the cache? Did it emit an outbox event? Did it log? Spy on those.
|
|
112
|
+
- **Error paths**: assert the exact AppError subclass thrown (`ConflictError`, `NotFoundError`), not just "an error was thrown".
|
|
113
|
+
|
|
114
|
+
## What NOT to do
|
|
115
|
+
|
|
116
|
+
- **Don't mock pg/Drizzle directly** in unit tests. Either go fake-DB (a hand-rolled stub object) or testcontainers. Mocking Drizzle leads to tests that pass but don't reflect SQL behavior.
|
|
117
|
+
- **Don't share mutable state across tests.** Each test gets a fresh fixture. If setup is expensive, use `beforeAll` + a reset in `beforeEach`.
|
|
118
|
+
- **Don't test framework internals** — those are tested in `@sixthsummit/ship-api`'s own test suite. You test your code.
|
|
119
|
+
- **Don't add tests just for coverage.** A test that asserts trivia (`expect(x).toBeDefined()`) is worse than no test — it lies about what's verified.
|
|
120
|
+
|
|
121
|
+
## Coverage
|
|
122
|
+
|
|
123
|
+
The template ships with no enforced coverage gate; opt in by editing `vitest.config.ts` if you want a floor. The framework itself targets 80% line / 70% branch on `packages/core/src/**` per `docs/V4_STATUS.md` — your app can do whatever fits the team.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
alwaysApply: true
|
|
3
|
+
description: "TypeScript coding standards for ShipAPI v4 apps"
|
|
4
|
+
tags: ["typescript", "type-safety"]
|
|
5
|
+
globs: ["src/**/*.ts", "tests/**/*.ts"]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# TypeScript Coding Standards
|
|
9
|
+
|
|
10
|
+
## Strictness
|
|
11
|
+
|
|
12
|
+
- The template runs with `strict: true`. Don't disable individual flags (`strictNullChecks`, `noImplicitAny`, etc.) per-file or per-tsconfig.
|
|
13
|
+
- `tsc --noEmit` runs in `npm run type-check` and CI. Type errors block the pipeline.
|
|
14
|
+
|
|
15
|
+
## `any`, `unknown`, `never`
|
|
16
|
+
|
|
17
|
+
- **No `any`.** Use `unknown` and narrow with `instanceof`, type guards, or Zod parsing.
|
|
18
|
+
- Use `unknown` for genuinely-unknown payloads (webhook bodies before signature verification, third-party callbacks). Narrow via Zod before use.
|
|
19
|
+
- Reach for `never` to mark exhaustiveness checks: `const _exhaustive: never = x;` in a `switch` default fails compile if a new case is added.
|
|
20
|
+
|
|
21
|
+
## Imports
|
|
22
|
+
|
|
23
|
+
- Import the framework via package-export paths: `@sixthsummit/ship-api`, `@sixthsummit/ship-api/client`, `@sixthsummit/ship-api/observability/logger`, etc.
|
|
24
|
+
- **Never import from `node_modules/@sixthsummit/ship-api/lib/`.** Those are build artifacts; the path is not part of the public API.
|
|
25
|
+
- Within your app, use relative imports for module-internal references and absolute paths (configured in `tsconfig.json` via `paths`) for cross-module references.
|
|
26
|
+
|
|
27
|
+
## Type-driven schema flow
|
|
28
|
+
|
|
29
|
+
ShipAPI v4 propagates types end-to-end via:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Drizzle table (schema.ts)
|
|
33
|
+
↓ drizzle-zod
|
|
34
|
+
Zod schema (validation/schemas.ts)
|
|
35
|
+
↓ validate("json", schema)
|
|
36
|
+
Hono route handler (c.req.valid("json"))
|
|
37
|
+
↓ typeof app
|
|
38
|
+
ShipApiClient type (exported from src/index.ts)
|
|
39
|
+
↓ hc<ShipApiClient>(BASE_URL)
|
|
40
|
+
Typed RPC client on the consumer
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This means: **schema changes propagate through TypeScript types alone, no codegen.** Don't add manual type duplications between layers — let inference flow.
|
|
44
|
+
|
|
45
|
+
## Null safety
|
|
46
|
+
|
|
47
|
+
- Prefer `T | undefined` for "not yet known" / "may not be present"; use `T | null` only when the database column is nullable (or another external system distinguishes null from absent).
|
|
48
|
+
- Don't use the non-null assertion `!` to suppress null errors. If TS thinks a value can be null, narrow it with a check or fix the type.
|
|
49
|
+
- Optional chaining (`?.`) and nullish coalescing (`??`) over `&&` / `||` for default-value patterns when you mean "null or undefined", not "any falsy".
|
|
50
|
+
|
|
51
|
+
## Branded types
|
|
52
|
+
|
|
53
|
+
For IDs and other opaque scalars where mixing types would be a bug:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
type AccountId = string & { readonly _brand: "AccountId" };
|
|
57
|
+
type PostId = string & { readonly _brand: "PostId" };
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then `getPost(accountId)` fails to compile. Branded types are zero-cost at runtime.
|
|
61
|
+
|
|
62
|
+
## Generics
|
|
63
|
+
|
|
64
|
+
- Default to inference. Explicit generics belong on factory APIs (`createApp<TSchema>`), not on call sites where TS can infer.
|
|
65
|
+
- Constrain generics with `extends` where it documents intent: `function fn<T extends { id: string }>(x: T)`.
|
|
66
|
+
- Avoid `any` as a generic constraint. `unknown` is almost always better.
|
|
67
|
+
|
|
68
|
+
## Error narrowing
|
|
69
|
+
|
|
70
|
+
Use `instanceof` against the AppError taxonomy when you need to react to specific errors:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
try { await service.create(input); }
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (err instanceof ConflictError) return c.json({ message: "exists" }, 409);
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Otherwise let `registerErrorHandlers(app)` handle it — don't catch errors just to re-throw.
|
|
81
|
+
|
|
82
|
+
## Conventions
|
|
83
|
+
|
|
84
|
+
- File names: `PascalCase` for classes / services (`PostService.ts`), `camelCase` for utilities (`buildQuery.ts`), `kebab-case` for route directories.
|
|
85
|
+
- Exported types from a module: name the type after what it represents (`Post`, `CreatePostInput`), not how it's derived (`PostType`, `CreatePostBody`).
|
|
86
|
+
- Prefer `type` over `interface` unless you specifically need declaration merging.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Required ─────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
# Postgres connection string — used by both runtime + drizzle-kit.
|
|
4
|
+
DATABASE_URL=postgres://ship_api:ship_api@localhost:5432/ship_api
|
|
5
|
+
|
|
6
|
+
# HMAC-SHA256 secret for OAuth state signing. ≥32 random bytes,
|
|
7
|
+
# rotated separately from JWT keys. Do not commit.
|
|
8
|
+
#
|
|
9
|
+
# Zero-downtime rotation: the framework's `StateSecret` type accepts
|
|
10
|
+
# either a single string OR an ordered array (head signs, tail still
|
|
11
|
+
# verifies). During a rotation window, source two env vars in your
|
|
12
|
+
# environment.ts and pass `[env.OAUTH_STATE_SECRET_NEW, env.OAUTH_STATE_SECRET_OLD]`
|
|
13
|
+
# as `stateSecret` to defineOAuthRoutes; once the rotation window
|
|
14
|
+
# (default 10 min) has fully drained, drop OLD from the array.
|
|
15
|
+
OAUTH_STATE_SECRET=replace-with-32-random-bytes
|
|
16
|
+
|
|
17
|
+
# JWT signing — ES256 key pair. Rotate via add-new-key →
|
|
18
|
+
# cut-signing → drain-old-key. The signing key is private; the
|
|
19
|
+
# verification keys are a JSON array of JWKS supporting >1 key
|
|
20
|
+
# during rotation windows.
|
|
21
|
+
JWT_SIGNING_KEY=
|
|
22
|
+
JWT_VERIFICATION_KEYS=[]
|
|
23
|
+
|
|
24
|
+
# Optional ─────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
PORT=3000
|
|
27
|
+
NODE_ENV=development
|
|
28
|
+
LOG_LEVEL=info
|
|
29
|
+
|
|
30
|
+
# CORS allowlist (comma-separated). When set, the framework wires
|
|
31
|
+
# `hono/cors` with `credentials: true` against this list. Required if
|
|
32
|
+
# your TypeScript client lives on a different origin from the API.
|
|
33
|
+
#
|
|
34
|
+
# `CORS_ORIGINS=*` is NOT supported with cookie auth — browsers reject
|
|
35
|
+
# the wildcard + credentials combo, and `createApp()` throws at boot
|
|
36
|
+
# rather than letting the misconfig sit silently. Use explicit origins:
|
|
37
|
+
#
|
|
38
|
+
# CORS_ORIGINS=https://app.example.com,https://admin.example.com
|
|
39
|
+
#
|
|
40
|
+
# Leave unset for a single-origin deployment (same domain for API + web).
|
|
41
|
+
CORS_ORIGINS=
|
|
42
|
+
|
|
43
|
+
# OpenTelemetry — disable in test, enable + point at a collector
|
|
44
|
+
# in prod. The auto-instrumentations cover HTTP / pg / ioredis /
|
|
45
|
+
# undici / pino with no per-call wiring.
|
|
46
|
+
OTEL_SDK_DISABLED=true
|
|
47
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=
|
|
48
|
+
OTEL_SERVICE_NAME=ship-api-app
|
|
49
|
+
ENVIRONMENT_NAME=development
|
|
50
|
+
|
|
51
|
+
# Prometheus metrics exporter (independent port, off by default).
|
|
52
|
+
METRICS_ENABLED=false
|
|
53
|
+
METRICS_PORT=9464
|
|
54
|
+
|
|
55
|
+
# Redis — required when using rate limiting / cache / BullMQ.
|
|
56
|
+
REDIS_URL=redis://localhost:6379
|
|
57
|
+
|
|
58
|
+
# Admin API key for the Bull-Board dashboard (and other admin
|
|
59
|
+
# surfaces). ≥32 random bytes. Required only when you mount the
|
|
60
|
+
# dashboard via `createBullBoardApp({ apiKey: env.ADMIN_API_KEY })`.
|
|
61
|
+
# DO NOT expose the dashboard on a public hostname even with the key
|
|
62
|
+
# set — operators should additionally guard at the network edge
|
|
63
|
+
# (private subnet / VPN / ingress allowlist).
|
|
64
|
+
ADMIN_API_KEY=
|
|
65
|
+
|
|
66
|
+
# Firebase auth (the v4.0 default AuthProvider). Replace with
|
|
67
|
+
# Better-Auth / Clerk / WorkOS / Auth.js adapter creds when you
|
|
68
|
+
# swap providers.
|
|
69
|
+
FIREBASE_CREDENTIALS=
|
|
70
|
+
|
|
71
|
+
# OAuth provider credentials. Add only the providers your app
|
|
72
|
+
# supports — the credentials resolver returns null for unconfigured
|
|
73
|
+
# providers and the routes layer 400s with a clear message.
|
|
74
|
+
OAUTH_GOOGLE_CLIENT_ID=
|
|
75
|
+
OAUTH_GOOGLE_CLIENT_SECRET=
|
|
76
|
+
OAUTH_GOOGLE_REDIRECT_URI=http://localhost:3000/oauth/google/callback
|
|
77
|
+
OAUTH_MICROSOFT_CLIENT_ID=
|
|
78
|
+
OAUTH_MICROSOFT_CLIENT_SECRET=
|
|
79
|
+
OAUTH_MICROSOFT_REDIRECT_URI=http://localhost:3000/oauth/microsoft/callback
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
name: Sherpa QA brief
|
|
2
|
+
|
|
3
|
+
# Generates a plain-English QA brief for the Sherpa toolkit:
|
|
4
|
+
# - On PR-to-dev: posts (or updates) a preview brief as a sticky PR comment.
|
|
5
|
+
# - On push to dev (i.e. merge to dev → deploy to dev site): posts the final
|
|
6
|
+
# brief to the QA Slack channel + uploads brief.md as a workflow artifact.
|
|
7
|
+
#
|
|
8
|
+
# The brief is produced by the ship-api plugin's `qa-brief` agent running
|
|
9
|
+
# headless via the Claude Code CLI. Engineers do nothing — Cursor and Claude
|
|
10
|
+
# Code users both get the same output through this CI path.
|
|
11
|
+
#
|
|
12
|
+
# Secrets required:
|
|
13
|
+
# ANTHROPIC_API_KEY — Claude API key for the headless agent run
|
|
14
|
+
# SLACK_QA_WEBHOOK_URL — Incoming Webhook for the QA channel (only used on push)
|
|
15
|
+
# GH_PACKAGES_TOKEN — read-access to @sixthsummit npm scope (if private deps)
|
|
16
|
+
#
|
|
17
|
+
# Override `STACK_LABEL` per repo if you customize the stack identity.
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
pull_request:
|
|
21
|
+
branches: [dev]
|
|
22
|
+
types: [opened, synchronize, reopened]
|
|
23
|
+
push:
|
|
24
|
+
branches: [dev]
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
pull-requests: write
|
|
29
|
+
|
|
30
|
+
concurrency:
|
|
31
|
+
group: sherpa-brief-${{ github.ref }}
|
|
32
|
+
cancel-in-progress: true
|
|
33
|
+
|
|
34
|
+
env:
|
|
35
|
+
NODE_VERSION: '20'
|
|
36
|
+
STACK_LABEL: 'ShipAPI (backend)'
|
|
37
|
+
|
|
38
|
+
jobs:
|
|
39
|
+
brief:
|
|
40
|
+
name: Generate QA brief
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
if: github.event.pull_request.draft != true
|
|
43
|
+
steps:
|
|
44
|
+
- name: Checkout
|
|
45
|
+
uses: actions/checkout@v4
|
|
46
|
+
with:
|
|
47
|
+
fetch-depth: 0
|
|
48
|
+
|
|
49
|
+
- name: Setup Node.js
|
|
50
|
+
uses: actions/setup-node@v4
|
|
51
|
+
with:
|
|
52
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
53
|
+
cache: 'npm'
|
|
54
|
+
|
|
55
|
+
- name: Resolve diff range and PR context
|
|
56
|
+
id: refs
|
|
57
|
+
env:
|
|
58
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
59
|
+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
60
|
+
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
61
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
62
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
63
|
+
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
|
|
64
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
65
|
+
PUSH_BEFORE: ${{ github.event.before }}
|
|
66
|
+
PUSH_AFTER: ${{ github.sha }}
|
|
67
|
+
PUSH_BRANCH: ${{ github.ref_name }}
|
|
68
|
+
run: |
|
|
69
|
+
set -euo pipefail
|
|
70
|
+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
|
|
71
|
+
echo "base=$PR_BASE_SHA" >> "$GITHUB_OUTPUT"
|
|
72
|
+
echo "head=$PR_HEAD_SHA" >> "$GITHUB_OUTPUT"
|
|
73
|
+
echo "branch=$PR_BRANCH" >> "$GITHUB_OUTPUT"
|
|
74
|
+
echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT"
|
|
75
|
+
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
|
76
|
+
printf '%s' "$PR_BODY" > pr_body.txt
|
|
77
|
+
else
|
|
78
|
+
BASE="$PUSH_BEFORE"
|
|
79
|
+
if [[ -z "$BASE" || "$BASE" == "0000000000000000000000000000000000000000" ]]; then
|
|
80
|
+
BASE="$(git rev-parse HEAD~1)"
|
|
81
|
+
fi
|
|
82
|
+
echo "base=$BASE" >> "$GITHUB_OUTPUT"
|
|
83
|
+
echo "head=$PUSH_AFTER" >> "$GITHUB_OUTPUT"
|
|
84
|
+
echo "branch=$PUSH_BRANCH" >> "$GITHUB_OUTPUT"
|
|
85
|
+
git log -1 --pretty=%s "$PUSH_AFTER" > .commit_subject
|
|
86
|
+
echo "pr_title=$(cat .commit_subject)" >> "$GITHUB_OUTPUT"
|
|
87
|
+
echo "pr_number=" >> "$GITHUB_OUTPUT"
|
|
88
|
+
git log -1 --pretty=%b "$PUSH_AFTER" > pr_body.txt
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
- name: Install Claude Code CLI
|
|
92
|
+
run: npm install -g @anthropic-ai/claude-code
|
|
93
|
+
|
|
94
|
+
- name: Run qa-brief agent
|
|
95
|
+
env:
|
|
96
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
97
|
+
BASE_REF: ${{ steps.refs.outputs.base }}
|
|
98
|
+
HEAD_REF: ${{ steps.refs.outputs.head }}
|
|
99
|
+
PR_TITLE: ${{ steps.refs.outputs.pr_title }}
|
|
100
|
+
PR_NUMBER: ${{ steps.refs.outputs.pr_number }}
|
|
101
|
+
REPO: ${{ github.repository }}
|
|
102
|
+
BRANCH: ${{ steps.refs.outputs.branch }}
|
|
103
|
+
STACK_LABEL: ${{ env.STACK_LABEL }}
|
|
104
|
+
run: |
|
|
105
|
+
set -euo pipefail
|
|
106
|
+
export PR_BODY="$(cat pr_body.txt)"
|
|
107
|
+
claude -p "Invoke the qa-brief subagent. Use BASE_REF=$BASE_REF and HEAD_REF=$HEAD_REF. Output ONLY the brief markdown — no preamble, no commentary, no fenced code wrapper around the whole thing. The header must use STACK_LABEL=\"$STACK_LABEL\", REPO=\"$REPO\", BRANCH=\"$BRANCH\", and PR=\"$PR_NUMBER\"." > brief.md
|
|
108
|
+
echo '--- generated brief ---'
|
|
109
|
+
cat brief.md
|
|
110
|
+
|
|
111
|
+
- name: Upload brief artifact
|
|
112
|
+
uses: actions/upload-artifact@v4
|
|
113
|
+
with:
|
|
114
|
+
name: sherpa-brief-${{ github.run_id }}
|
|
115
|
+
path: brief.md
|
|
116
|
+
retention-days: 30
|
|
117
|
+
|
|
118
|
+
- name: Post sticky PR comment
|
|
119
|
+
if: github.event_name == 'pull_request'
|
|
120
|
+
uses: marocchino/sticky-pull-request-comment@v2
|
|
121
|
+
with:
|
|
122
|
+
header: sherpa-brief
|
|
123
|
+
path: brief.md
|
|
124
|
+
|
|
125
|
+
- name: Notify Slack on merge to dev
|
|
126
|
+
if: github.event_name == 'push'
|
|
127
|
+
env:
|
|
128
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_QA_WEBHOOK_URL }}
|
|
129
|
+
STACK_LABEL: ${{ env.STACK_LABEL }}
|
|
130
|
+
run: |
|
|
131
|
+
set -euo pipefail
|
|
132
|
+
if [[ -z "${SLACK_WEBHOOK_URL:-}" ]]; then
|
|
133
|
+
echo "::warning::SLACK_QA_WEBHOOK_URL not set; skipping Slack notification."
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
ARTIFACT_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
137
|
+
COMMIT_URL="${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}"
|
|
138
|
+
TITLE="$(head -1 brief.md | sed 's/^# *//')"
|
|
139
|
+
# Slack section text caps at ~3000 chars; truncate the inline snippet
|
|
140
|
+
# and rely on the artifact link for the full brief.
|
|
141
|
+
PREVIEW="$(head -c 2500 brief.md)"
|
|
142
|
+
if [[ "$(wc -c < brief.md)" -gt 2500 ]]; then
|
|
143
|
+
PREVIEW="${PREVIEW}
|
|
144
|
+
|
|
145
|
+
… (truncated — open the full brief from the link below)"
|
|
146
|
+
fi
|
|
147
|
+
jq -n \
|
|
148
|
+
--arg title "$TITLE" \
|
|
149
|
+
--arg stack "$STACK_LABEL" \
|
|
150
|
+
--arg repo "${{ github.repository }}" \
|
|
151
|
+
--arg branch "${{ steps.refs.outputs.branch }}" \
|
|
152
|
+
--arg artifact "$ARTIFACT_URL" \
|
|
153
|
+
--arg commit "$COMMIT_URL" \
|
|
154
|
+
--arg preview "$PREVIEW" \
|
|
155
|
+
'{
|
|
156
|
+
text: ("New QA brief: " + $title),
|
|
157
|
+
blocks: [
|
|
158
|
+
{ "type": "header",
|
|
159
|
+
"text": { "type": "plain_text", "text": $title } },
|
|
160
|
+
{ "type": "section",
|
|
161
|
+
"fields": [
|
|
162
|
+
{ "type": "mrkdwn", "text": ("*Stack:*\n" + $stack) },
|
|
163
|
+
{ "type": "mrkdwn", "text": ("*Repo:*\n" + $repo) },
|
|
164
|
+
{ "type": "mrkdwn", "text": ("*Branch:*\n`" + $branch + "`") }
|
|
165
|
+
] },
|
|
166
|
+
{ "type": "section",
|
|
167
|
+
"text": { "type": "mrkdwn", "text": $preview } },
|
|
168
|
+
{ "type": "actions",
|
|
169
|
+
"elements": [
|
|
170
|
+
{ "type": "button",
|
|
171
|
+
"text": { "type": "plain_text", "text": "Open full brief" },
|
|
172
|
+
"url": $artifact },
|
|
173
|
+
{ "type": "button",
|
|
174
|
+
"text": { "type": "plain_text", "text": "View commit" },
|
|
175
|
+
"url": $commit }
|
|
176
|
+
] }
|
|
177
|
+
]
|
|
178
|
+
}' > slack-payload.json
|
|
179
|
+
curl -sS --fail -X POST -H 'Content-Type: application/json' \
|
|
180
|
+
--data @slack-payload.json "$SLACK_WEBHOOK_URL"
|