@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,147 @@
|
|
|
1
|
+
import { DrizzleEntityService } from "@sixthsummit/ship-api/app/services/DrizzleEntityService";
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-restricted-imports -- conditional provider/account lookups + upsert; candidate for a QueryOptions refactor
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import {
|
|
5
|
+
authorizationHeader,
|
|
6
|
+
isActive,
|
|
7
|
+
isExpired,
|
|
8
|
+
isExpiringSoon,
|
|
9
|
+
needsRefresh,
|
|
10
|
+
type OAuthConnection,
|
|
11
|
+
oauthConnections,
|
|
12
|
+
} from "../schema.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* v4 `OAuthConnectionService` — extends `DrizzleEntityService` for
|
|
16
|
+
* CRUD on the oauth_connections table, plus connection-management
|
|
17
|
+
* helpers that v3 spread across `OAuthService.ts`.
|
|
18
|
+
*
|
|
19
|
+
* Inherits get / getAll / list / count / create / update / delete
|
|
20
|
+
* (with soft-delete + audit-column auto-stamping) from the base. Adds:
|
|
21
|
+
*
|
|
22
|
+
* - findByAccountAndProvider(accountId, providerCode)
|
|
23
|
+
* - upsertTokens(...) — atomic insert-or-update on the unique
|
|
24
|
+
* (accountId, providerCode) composite, used by adapter callbacks
|
|
25
|
+
* - clearTokens(id) — disconnect: nulls out access/refresh/expires
|
|
26
|
+
* without deleting the row (preserves provider linkage history)
|
|
27
|
+
* - status helpers re-exposed: isExpired / isExpiringSoon /
|
|
28
|
+
* isActive / needsRefresh / authorizationHeader (delegate to the
|
|
29
|
+
* module-level pure functions in schema.ts)
|
|
30
|
+
*
|
|
31
|
+
* Token rotation flows in via `upsertTokens` from adapter callbacks
|
|
32
|
+
* (`oauth/{provider}/callback`); refresh flows in via the same path
|
|
33
|
+
* from a worker that polls `expiresAt` indexes for soon-to-expire
|
|
34
|
+
* tokens.
|
|
35
|
+
*/
|
|
36
|
+
export class OAuthConnectionService extends DrizzleEntityService<
|
|
37
|
+
typeof oauthConnections,
|
|
38
|
+
{ oauthConnections: typeof oauthConnections }
|
|
39
|
+
> {
|
|
40
|
+
protected getTable() {
|
|
41
|
+
return oauthConnections;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected override getDefaultSearchFields(): readonly string[] {
|
|
45
|
+
return ["providerCode", "providerAccountId"];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected override getDefaultRelations(): readonly string[] {
|
|
49
|
+
return ["account"];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Look up the row keyed by (accountId, providerCode). Returns `null`
|
|
54
|
+
* when the account hasn't connected the provider.
|
|
55
|
+
*/
|
|
56
|
+
async findByAccountAndProvider(
|
|
57
|
+
accountId: string,
|
|
58
|
+
providerCode: string,
|
|
59
|
+
): Promise<OAuthConnection | null> {
|
|
60
|
+
const rows = await this.db
|
|
61
|
+
.select()
|
|
62
|
+
.from(oauthConnections)
|
|
63
|
+
.where(
|
|
64
|
+
and(
|
|
65
|
+
eq(oauthConnections.accountId, accountId),
|
|
66
|
+
eq(oauthConnections.providerCode, providerCode),
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
.limit(1);
|
|
70
|
+
return (rows[0] as OAuthConnection | undefined) ?? null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Upsert pattern keyed on the unique (accountId, providerCode)
|
|
75
|
+
* composite — used by adapter callback handlers when a user grants
|
|
76
|
+
* access (insert) or re-grants (update). Drizzle's `onConflictDoUpdate`
|
|
77
|
+
* matches the unique index and updates the same fields on conflict.
|
|
78
|
+
*/
|
|
79
|
+
async upsertTokens(values: {
|
|
80
|
+
accountId: string;
|
|
81
|
+
providerCode: string;
|
|
82
|
+
accessToken?: string;
|
|
83
|
+
refreshToken?: string;
|
|
84
|
+
expiresAt?: Date;
|
|
85
|
+
scope?: string;
|
|
86
|
+
tokenType?: string;
|
|
87
|
+
providerAccountId?: string;
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}): Promise<OAuthConnection> {
|
|
90
|
+
const insertValues = {
|
|
91
|
+
accountId: values.accountId,
|
|
92
|
+
providerCode: values.providerCode,
|
|
93
|
+
accessToken: values.accessToken ?? null,
|
|
94
|
+
refreshToken: values.refreshToken ?? null,
|
|
95
|
+
expiresAt: values.expiresAt ?? null,
|
|
96
|
+
scope: values.scope ?? null,
|
|
97
|
+
tokenType: values.tokenType ?? null,
|
|
98
|
+
providerAccountId: values.providerAccountId ?? null,
|
|
99
|
+
metadata: values.metadata ?? {},
|
|
100
|
+
};
|
|
101
|
+
const [row] = await this.db
|
|
102
|
+
.insert(oauthConnections)
|
|
103
|
+
.values(insertValues as never)
|
|
104
|
+
.onConflictDoUpdate({
|
|
105
|
+
target: [oauthConnections.accountId, oauthConnections.providerCode],
|
|
106
|
+
set: {
|
|
107
|
+
accessToken: insertValues.accessToken,
|
|
108
|
+
refreshToken: insertValues.refreshToken,
|
|
109
|
+
expiresAt: insertValues.expiresAt,
|
|
110
|
+
scope: insertValues.scope,
|
|
111
|
+
tokenType: insertValues.tokenType,
|
|
112
|
+
providerAccountId: insertValues.providerAccountId,
|
|
113
|
+
metadata: insertValues.metadata,
|
|
114
|
+
} as never,
|
|
115
|
+
})
|
|
116
|
+
.returning();
|
|
117
|
+
return row as OAuthConnection;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Clear tokens (disconnect) without deleting the row — preserves the
|
|
122
|
+
* provider-account-ID linkage so a later reconnect doesn't create a
|
|
123
|
+
* stale duplicate.
|
|
124
|
+
*/
|
|
125
|
+
async clearTokens(id: string): Promise<OAuthConnection | null> {
|
|
126
|
+
const [row] = await this.db
|
|
127
|
+
.update(oauthConnections)
|
|
128
|
+
.set({
|
|
129
|
+
accessToken: null,
|
|
130
|
+
refreshToken: null,
|
|
131
|
+
expiresAt: null,
|
|
132
|
+
scope: null,
|
|
133
|
+
tokenType: null,
|
|
134
|
+
} as never)
|
|
135
|
+
.where(eq(oauthConnections.id, id))
|
|
136
|
+
.returning();
|
|
137
|
+
return (row as OAuthConnection | undefined) ?? null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Re-export the module-level status helpers so callers don't have to
|
|
141
|
+
// import them separately when they already hold a service reference.
|
|
142
|
+
isExpired = isExpired;
|
|
143
|
+
isExpiringSoon = isExpiringSoon;
|
|
144
|
+
isActive = isActive;
|
|
145
|
+
needsRefresh = needsRefresh;
|
|
146
|
+
authorizationHeader = authorizationHeader;
|
|
147
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Opaque state primitive for the OAuth `state` parameter.
|
|
5
|
+
*
|
|
6
|
+
* The framework owns this end-to-end: adapters round-trip the string
|
|
7
|
+
* verbatim, never inspect it. The routes layer:
|
|
8
|
+
*
|
|
9
|
+
* 1. Builds a `StatePayload` describing what user is connecting and
|
|
10
|
+
* to which provider.
|
|
11
|
+
* 2. Calls `signState(payload, secret)` → opaque token to embed in
|
|
12
|
+
* the authorize URL.
|
|
13
|
+
* 3. On callback, calls `verifyState(state, secret)` → recovers the
|
|
14
|
+
* payload, throws on tamper / expiry.
|
|
15
|
+
*
|
|
16
|
+
* Encoding: `base64url(payloadJson) . base64url(hmacSha256(payloadJson, secret))`
|
|
17
|
+
*
|
|
18
|
+
* Why HMAC-SHA256 instead of ES256/JWT:
|
|
19
|
+
* - State payloads are tiny (account-id + provider + nonce + ts);
|
|
20
|
+
* JWT's overhead doesn't add value.
|
|
21
|
+
* - HMAC-SHA256 is timing-safe-comparable in stdlib without
|
|
22
|
+
* additional ceremony.
|
|
23
|
+
* - No external key-rotation infrastructure needed; secret comes
|
|
24
|
+
* from the framework's existing JWT signing-key plumbing.
|
|
25
|
+
*
|
|
26
|
+
* Replay protection: timestamp window (default 10 minutes). The OAuth
|
|
27
|
+
* `code` itself is single-use at the provider — the upstream rejects
|
|
28
|
+
* code reuse even if state were replayable. The window is the
|
|
29
|
+
* defense against a leaked state being used minutes later by the
|
|
30
|
+
* attacker before the legitimate user finishes the flow.
|
|
31
|
+
*
|
|
32
|
+
* Tamper protection: HMAC over the full payload-JSON. Any byte change
|
|
33
|
+
* in the encoded payload fails verification.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export interface StatePayload {
|
|
37
|
+
/** Framework-owned subject — typically `accounts.id`. */
|
|
38
|
+
accountId: string;
|
|
39
|
+
/** Provider being connected, matches `providerCode` on the connection row. */
|
|
40
|
+
providerCode: string;
|
|
41
|
+
/**
|
|
42
|
+
* Caller-defined return-target after callback completes (e.g. the
|
|
43
|
+
* page the user was on when they hit "connect"). Sanitize at the
|
|
44
|
+
* routes layer before redirecting.
|
|
45
|
+
*/
|
|
46
|
+
returnTo?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Free-form caller-defined extras the framework round-trips. Used
|
|
49
|
+
* by routes that initiate OAuth as part of a larger flow (e.g.
|
|
50
|
+
* onboarding wizard step IDs).
|
|
51
|
+
*/
|
|
52
|
+
extras?: Record<string, unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* PKCE code-verifier (raw, base64url-encoded, 43–128 chars). The
|
|
55
|
+
* framework generates this in the authorize handler, derives the
|
|
56
|
+
* SHA-256 challenge for the upstream provider, and signs the
|
|
57
|
+
* verifier into state so the callback can pass it back to the
|
|
58
|
+
* adapter's `exchangeCodeForTokens` (F12).
|
|
59
|
+
*/
|
|
60
|
+
codeVerifier?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Per-state opaque nonce, populated by `signState` (16 random
|
|
63
|
+
* base64url bytes). `signState` ignores this when supplied by
|
|
64
|
+
* the caller — it always generates a fresh nonce. `verifyState`
|
|
65
|
+
* surfaces the recovered nonce so the routes layer can use it as
|
|
66
|
+
* a single-use JTI against a short-TTL replay cache (F16).
|
|
67
|
+
*/
|
|
68
|
+
nonce?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface SignedStatePayload extends StatePayload {
|
|
72
|
+
/** Issued-at unix milliseconds. */
|
|
73
|
+
iat: number;
|
|
74
|
+
/** Opaque random nonce — defends against replay within the window. */
|
|
75
|
+
nonce: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class InvalidStateError extends Error {
|
|
79
|
+
constructor(reason: string) {
|
|
80
|
+
super(`OAuth state invalid: ${reason}`);
|
|
81
|
+
this.name = "InvalidStateError";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SignStateOptions {
|
|
86
|
+
/** Override the unix-ms-now used for `iat`. Tests inject a fixed clock. */
|
|
87
|
+
now?: () => number;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface VerifyStateOptions {
|
|
91
|
+
/**
|
|
92
|
+
* Acceptable age in milliseconds. Default 10 minutes. The state is
|
|
93
|
+
* rejected when (now - iat) exceeds this.
|
|
94
|
+
*/
|
|
95
|
+
maxAgeMs?: number;
|
|
96
|
+
/** Override the now-source. Tests pin time. */
|
|
97
|
+
now?: () => number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const DEFAULT_MAX_AGE_MS = 10 * 60 * 1000;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* State-signing secret. A single string is the simple path; an ordered
|
|
104
|
+
* array enables zero-downtime rotation — the **head** is the active
|
|
105
|
+
* signing key, every entry is accepted on verify. Standard rotation
|
|
106
|
+
* pattern:
|
|
107
|
+
*
|
|
108
|
+
* 1. Prepend the new key to the head: `[ "new", "old" ]`.
|
|
109
|
+
* 2. Wait at least `maxAgeMs` (default 10 minutes) for any in-flight
|
|
110
|
+
* old-key states to expire.
|
|
111
|
+
* 3. Drop the old key from the tail: `[ "new" ]`.
|
|
112
|
+
*
|
|
113
|
+
* `defineEnv` is the boundary that turns env vars into this shape;
|
|
114
|
+
* consumers typically expose `OAUTH_STATE_SECRET` as a single string
|
|
115
|
+
* during steady-state and switch to a JSON-array env (or split into
|
|
116
|
+
* `OAUTH_STATE_SECRET_NEW` + `OAUTH_STATE_SECRET_OLD` parsed into an
|
|
117
|
+
* array in `environment.ts`) during a rotation window.
|
|
118
|
+
*/
|
|
119
|
+
export type StateSecret = string | readonly string[];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Returns the head (active signing) key. Throws when no usable key is
|
|
123
|
+
* available — empty string, empty array, or array of empty strings.
|
|
124
|
+
*/
|
|
125
|
+
function resolveSigningKey(secret: StateSecret): string {
|
|
126
|
+
if (typeof secret === "string") {
|
|
127
|
+
if (!secret) throw new Error("signState: secret is required");
|
|
128
|
+
return secret;
|
|
129
|
+
}
|
|
130
|
+
for (const candidate of secret) {
|
|
131
|
+
if (typeof candidate === "string" && candidate.length > 0) return candidate;
|
|
132
|
+
}
|
|
133
|
+
throw new Error("signState: secret is required");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Returns every non-empty key in the verification set, head first.
|
|
138
|
+
* Throws when the set is empty (same error as v0.1's single-string
|
|
139
|
+
* "secret is required" path so existing callers see a familiar error).
|
|
140
|
+
*/
|
|
141
|
+
function resolveVerificationKeys(secret: StateSecret): readonly string[] {
|
|
142
|
+
if (typeof secret === "string") {
|
|
143
|
+
if (!secret) throw new Error("verifyState: secret is required");
|
|
144
|
+
return [secret];
|
|
145
|
+
}
|
|
146
|
+
const out = secret.filter((k) => typeof k === "string" && k.length > 0);
|
|
147
|
+
if (out.length === 0) throw new Error("verifyState: secret is required");
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function signState(
|
|
152
|
+
payload: StatePayload,
|
|
153
|
+
secret: StateSecret,
|
|
154
|
+
opts: SignStateOptions = {},
|
|
155
|
+
): string {
|
|
156
|
+
const signingKey = resolveSigningKey(secret);
|
|
157
|
+
const now = opts.now?.() ?? Date.now();
|
|
158
|
+
const signed: SignedStatePayload = {
|
|
159
|
+
...payload,
|
|
160
|
+
iat: now,
|
|
161
|
+
nonce: randomBytes(16).toString("base64url"),
|
|
162
|
+
};
|
|
163
|
+
const json = JSON.stringify(signed);
|
|
164
|
+
const payloadEncoded = Buffer.from(json, "utf8").toString("base64url");
|
|
165
|
+
const sig = createHmac("sha256", signingKey).update(payloadEncoded).digest("base64url");
|
|
166
|
+
return `${payloadEncoded}.${sig}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function verifyState(
|
|
170
|
+
state: string,
|
|
171
|
+
secret: StateSecret,
|
|
172
|
+
opts: VerifyStateOptions = {},
|
|
173
|
+
): StatePayload {
|
|
174
|
+
if (!state) throw new InvalidStateError("missing state");
|
|
175
|
+
const verificationKeys = resolveVerificationKeys(secret);
|
|
176
|
+
|
|
177
|
+
const dot = state.indexOf(".");
|
|
178
|
+
if (dot === -1) throw new InvalidStateError("malformed state");
|
|
179
|
+
const payloadEncoded = state.slice(0, dot);
|
|
180
|
+
const sigSupplied = state.slice(dot + 1);
|
|
181
|
+
const supplied = Buffer.from(sigSupplied, "base64url");
|
|
182
|
+
|
|
183
|
+
// Iterate every key to completion even after a match. Short-circuiting
|
|
184
|
+
// on first match would leak via wall-clock timing which position in
|
|
185
|
+
// the rotation set matched — narrowing the search for any known-leaked
|
|
186
|
+
// tail key. The keyset is small (1–3 in practice) and HMAC-SHA256 over
|
|
187
|
+
// a ~1KB payload is microseconds; the cost of constant-time is
|
|
188
|
+
// negligible. Per-iteration HMAC compare is already timing-safe via
|
|
189
|
+
// `timingSafeEqual`; this loop wraps it in a constant-iteration outer.
|
|
190
|
+
let matched = false;
|
|
191
|
+
for (const key of verificationKeys) {
|
|
192
|
+
const expected = createHmac("sha256", key).update(payloadEncoded).digest("base64url");
|
|
193
|
+
const expectedBytes = Buffer.from(expected, "base64url");
|
|
194
|
+
if (supplied.length === expectedBytes.length && timingSafeEqual(supplied, expectedBytes)) {
|
|
195
|
+
matched = true;
|
|
196
|
+
// No `break` — keep iterating to flatten the timing profile.
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (!matched) {
|
|
200
|
+
throw new InvalidStateError("signature mismatch");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let parsed: SignedStatePayload;
|
|
204
|
+
try {
|
|
205
|
+
const json = Buffer.from(payloadEncoded, "base64url").toString("utf8");
|
|
206
|
+
parsed = JSON.parse(json) as SignedStatePayload;
|
|
207
|
+
} catch {
|
|
208
|
+
throw new InvalidStateError("payload not valid JSON");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
typeof parsed.accountId !== "string" ||
|
|
213
|
+
typeof parsed.providerCode !== "string" ||
|
|
214
|
+
typeof parsed.iat !== "number" ||
|
|
215
|
+
typeof parsed.nonce !== "string"
|
|
216
|
+
) {
|
|
217
|
+
throw new InvalidStateError("payload missing required fields");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const now = opts.now?.() ?? Date.now();
|
|
221
|
+
const maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
222
|
+
const age = now - parsed.iat;
|
|
223
|
+
if (age < 0) throw new InvalidStateError("state issued in the future");
|
|
224
|
+
if (age > maxAgeMs) throw new InvalidStateError("state expired");
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
accountId: parsed.accountId,
|
|
228
|
+
providerCode: parsed.providerCode,
|
|
229
|
+
returnTo: parsed.returnTo,
|
|
230
|
+
extras: parsed.extras,
|
|
231
|
+
codeVerifier: parsed.codeVerifier,
|
|
232
|
+
nonce: parsed.nonce,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `StateJtiCache` — single-use bookkeeping for OAuth state nonces (F16).
|
|
3
|
+
*
|
|
4
|
+
* The framework's signed state already defends against tampering and
|
|
5
|
+
* carries a 10-minute expiry window. Inside that window, single-use
|
|
6
|
+
* is enforced *only* by the upstream provider invalidating the
|
|
7
|
+
* authorization code on exchange — fine when providers behave, but:
|
|
8
|
+
*
|
|
9
|
+
* - Some providers retry on idempotent failures (e.g. transient
|
|
10
|
+
* network errors on the framework's token-exchange request)
|
|
11
|
+
* instead of invalidating eagerly.
|
|
12
|
+
* - A downstream adapter retry against a failed exchange can resend
|
|
13
|
+
* the same state.
|
|
14
|
+
* - A leaked-then-replayed state from logs / browser history / a
|
|
15
|
+
* misconfigured proxy that records query strings is theoretically
|
|
16
|
+
* replayable until the 10-minute window closes.
|
|
17
|
+
*
|
|
18
|
+
* This cache closes the within-window window: every successful state
|
|
19
|
+
* verification records the nonce (acting as a JTI) with a short TTL.
|
|
20
|
+
* A second use of the same nonce is rejected as a replay.
|
|
21
|
+
*
|
|
22
|
+
* Default in-memory implementation; multi-instance deployments need
|
|
23
|
+
* a shared backing (Redis, etc.) to enforce single-use across nodes.
|
|
24
|
+
* Surface the interface so a custom implementation can plug in.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export interface StateJtiCache {
|
|
28
|
+
/**
|
|
29
|
+
* Atomic check + record. Returns `true` when `jti` was seen
|
|
30
|
+
* within the last `ttlMs` (treat as replay; reject); returns
|
|
31
|
+
* `false` when `jti` is fresh (record it and let the call
|
|
32
|
+
* proceed). Same-tick semantics: within a single event-loop tick,
|
|
33
|
+
* concurrent `seenAndRecord` calls behave like CAS (exactly one
|
|
34
|
+
* returns `false`).
|
|
35
|
+
*/
|
|
36
|
+
seenAndRecord(jti: string, ttlMs?: number): boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Default in-memory implementation. A `Map<string, number>` where
|
|
41
|
+
* the value is the entry's expiration epoch (ms). Pruned
|
|
42
|
+
* opportunistically on each call — no separate timer thread.
|
|
43
|
+
*
|
|
44
|
+
* Process lifecycle: entries reset on process restart. State has a
|
|
45
|
+
* 10-minute expiry, so a restart effectively widens the replay
|
|
46
|
+
* window to 10 minutes (not unlimited). Multi-instance consumers
|
|
47
|
+
* (behind a load balancer) need a shared cache to enforce
|
|
48
|
+
* single-use across nodes; swap in a Redis-backed implementation
|
|
49
|
+
* of `StateJtiCache` to do so.
|
|
50
|
+
*/
|
|
51
|
+
export class InMemoryStateJtiCache implements StateJtiCache {
|
|
52
|
+
private readonly entries = new Map<string, number>();
|
|
53
|
+
private lastPruneAt = 0;
|
|
54
|
+
|
|
55
|
+
/** Default TTL = 60 s (≈ longest reasonable OAuth round-trip). */
|
|
56
|
+
static readonly DEFAULT_TTL_MS = 60_000;
|
|
57
|
+
|
|
58
|
+
/** Run a prune at most every `PRUNE_INTERVAL_MS` to amortize cost. */
|
|
59
|
+
private static readonly PRUNE_INTERVAL_MS = 5_000;
|
|
60
|
+
|
|
61
|
+
seenAndRecord(jti: string, ttlMs = InMemoryStateJtiCache.DEFAULT_TTL_MS): boolean {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
this.maybePrune(now);
|
|
64
|
+
|
|
65
|
+
const existing = this.entries.get(jti);
|
|
66
|
+
if (existing !== undefined && existing > now) {
|
|
67
|
+
return true; // replay
|
|
68
|
+
}
|
|
69
|
+
this.entries.set(jti, now + ttlMs);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Exposed for tests; not part of the public contract. */
|
|
74
|
+
_size(): number {
|
|
75
|
+
return this.entries.size;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private maybePrune(now: number): void {
|
|
79
|
+
if (now - this.lastPruneAt < InMemoryStateJtiCache.PRUNE_INTERVAL_MS) return;
|
|
80
|
+
this.lastPruneAt = now;
|
|
81
|
+
for (const [jti, expiresAt] of this.entries) {
|
|
82
|
+
if (expiresAt <= now) this.entries.delete(jti);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { AuthenticationError } from "@sixthsummit/ship-api/http/errors/AuthenticationError";
|
|
2
|
+
import type { JWTPayload, JWTVerifyOptions } from "jose";
|
|
3
|
+
import { createRemoteJWKSet, errors as joseErrors, jwtVerify } from "jose";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Key resolver accepted by `jose.jwtVerify` — a literal key, a
|
|
7
|
+
* `JWKSet` instance returned from `createRemoteJWKSet` /
|
|
8
|
+
* `createLocalJWKSet`, or a callable that yields one per JWS
|
|
9
|
+
* header.
|
|
10
|
+
*/
|
|
11
|
+
export type IdTokenKeyInput = Parameters<typeof jwtVerify>[1];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `verifyIdToken` — OIDC `id_token` verification helper (F13).
|
|
15
|
+
*
|
|
16
|
+
* **`@internal` — no adapter currently consumes `id_token` as an
|
|
17
|
+
* identity decision.** The existing adapters resolve the provider
|
|
18
|
+
* account id via `/userinfo` over a Bearer-authenticated access
|
|
19
|
+
* token, not via id_token claims. This helper exists so any future
|
|
20
|
+
* code path that promotes id_token claims to identity decisions
|
|
21
|
+
* (e.g. `email_verified`, `sub` lookup without a userinfo round-trip)
|
|
22
|
+
* MUST route through it instead of decoding the JWT directly.
|
|
23
|
+
*
|
|
24
|
+
* Verifies, in order:
|
|
25
|
+
* 1. Signature against the provider's JWKS (RS256 / ES256 — `jose`
|
|
26
|
+
* picks the algorithm from the JWS header).
|
|
27
|
+
* 2. `iss` (issuer) matches `expectedIssuer`.
|
|
28
|
+
* 3. `aud` (audience) matches `expectedAudience`.
|
|
29
|
+
* 4. `exp` / `nbf` / `iat` against the current clock (configurable
|
|
30
|
+
* tolerance, default 30s — matches `JwtSigner`'s P0-F9 default).
|
|
31
|
+
* 5. `nonce` claim equals `expectedNonce` (constant-time string
|
|
32
|
+
* compare). This is the defense the helper is named for: without
|
|
33
|
+
* a nonce check, an attacker who captures a victim's id_token in
|
|
34
|
+
* one OIDC flow can replay it into a separate framework-initiated
|
|
35
|
+
* flow to impersonate the victim's identity decision.
|
|
36
|
+
*
|
|
37
|
+
* Throws `AuthenticationError` on any failure — caller surfaces this
|
|
38
|
+
* through the framework's standard error path.
|
|
39
|
+
*
|
|
40
|
+
* JWKS fetches are cached per-URI inside the module (`jose`'s
|
|
41
|
+
* `createRemoteJWKSet` does this internally — same `URL` value
|
|
42
|
+
* resolves to the same JWKS instance across the process lifetime).
|
|
43
|
+
*
|
|
44
|
+
* Future call-site contract: any code path that promotes id_token
|
|
45
|
+
* claims to a security decision (account lookup, `email_verified`
|
|
46
|
+
* gating, role assignment) MUST call this helper with the nonce
|
|
47
|
+
* the framework signed into state for the originating flow. The
|
|
48
|
+
* routes layer already round-trips a `nonce` field in the signed
|
|
49
|
+
* `StatePayload` — wire the nonce echo there when an adapter
|
|
50
|
+
* actually consumes id_token.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
export interface VerifyIdTokenOptions {
|
|
54
|
+
/** Raw id_token JWS (`xxxx.yyyy.zzzz`). */
|
|
55
|
+
idToken: string;
|
|
56
|
+
/**
|
|
57
|
+
* Provider's JWKS endpoint URL (e.g. `https://www.googleapis.com/oauth2/v3/certs`)
|
|
58
|
+
* **OR** a pre-built key/JWKS resolver from `jose`. Production
|
|
59
|
+
* code typically passes the URL string; tests + advanced callers
|
|
60
|
+
* pass a `createLocalJWKSet({ keys: [...] })` or a literal
|
|
61
|
+
* `CryptoKey` to avoid the network round-trip.
|
|
62
|
+
*
|
|
63
|
+
* When a URL string is supplied, the helper memoizes the
|
|
64
|
+
* `createRemoteJWKSet` instance per URL across the process so
|
|
65
|
+
* repeated calls against the same provider don't refetch keys per
|
|
66
|
+
* request.
|
|
67
|
+
*/
|
|
68
|
+
key: string | IdTokenKeyInput;
|
|
69
|
+
/**
|
|
70
|
+
* Nonce echoed from the framework-signed OAuth state. Compared
|
|
71
|
+
* against the JWT's `nonce` claim. Required — passing an empty
|
|
72
|
+
* string is a configuration error and rejects every token.
|
|
73
|
+
*/
|
|
74
|
+
expectedNonce: string;
|
|
75
|
+
/**
|
|
76
|
+
* Expected `iss` claim (e.g. `https://accounts.google.com`,
|
|
77
|
+
* `https://login.microsoftonline.com/<tenant>/v2.0`). The provider
|
|
78
|
+
* documents the exact issuer string.
|
|
79
|
+
*/
|
|
80
|
+
expectedIssuer: string;
|
|
81
|
+
/**
|
|
82
|
+
* Expected `aud` claim — typically the OAuth client_id for which
|
|
83
|
+
* the token was minted.
|
|
84
|
+
*/
|
|
85
|
+
expectedAudience: string;
|
|
86
|
+
/**
|
|
87
|
+
* Clock-skew tolerance in seconds. Default 30 — matches `JwtSigner`'s
|
|
88
|
+
* post-F9 default and NIST SP 800-63B §5.1.1.2 guidance.
|
|
89
|
+
*/
|
|
90
|
+
clockToleranceSec?: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const DEFAULT_CLOCK_TOLERANCE_SEC = 30;
|
|
94
|
+
|
|
95
|
+
const jwksCache: Map<string, ReturnType<typeof createRemoteJWKSet>> = new Map();
|
|
96
|
+
|
|
97
|
+
function jwksFor(uri: string): ReturnType<typeof createRemoteJWKSet> {
|
|
98
|
+
const cached = jwksCache.get(uri);
|
|
99
|
+
if (cached) return cached;
|
|
100
|
+
const set = createRemoteJWKSet(new URL(uri));
|
|
101
|
+
jwksCache.set(uri, set);
|
|
102
|
+
return set;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Constant-time string equality. Both inputs must be the same length
|
|
107
|
+
* to avoid early-return timing leaks. Mismatched lengths return false
|
|
108
|
+
* immediately — the nonce we generate is a fixed-length base64url
|
|
109
|
+
* value, so an attacker who supplies a shorter or longer string
|
|
110
|
+
* always loses on length alone.
|
|
111
|
+
*/
|
|
112
|
+
function constantTimeStringEqual(a: string, b: string): boolean {
|
|
113
|
+
if (a.length !== b.length) return false;
|
|
114
|
+
let diff = 0;
|
|
115
|
+
for (let i = 0; i < a.length; i++) {
|
|
116
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
117
|
+
}
|
|
118
|
+
return diff === 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function verifyIdToken(opts: VerifyIdTokenOptions): Promise<JWTPayload> {
|
|
122
|
+
if (!opts.idToken) throw new AuthenticationError("verifyIdToken: missing idToken");
|
|
123
|
+
if (!opts.expectedNonce) {
|
|
124
|
+
throw new AuthenticationError("verifyIdToken: expectedNonce is required");
|
|
125
|
+
}
|
|
126
|
+
if (!opts.expectedIssuer) {
|
|
127
|
+
throw new AuthenticationError("verifyIdToken: expectedIssuer is required");
|
|
128
|
+
}
|
|
129
|
+
if (!opts.expectedAudience) {
|
|
130
|
+
throw new AuthenticationError("verifyIdToken: expectedAudience is required");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const key: IdTokenKeyInput = typeof opts.key === "string" ? jwksFor(opts.key) : opts.key;
|
|
134
|
+
const verifyOpts: JWTVerifyOptions = {
|
|
135
|
+
issuer: opts.expectedIssuer,
|
|
136
|
+
audience: opts.expectedAudience,
|
|
137
|
+
clockTolerance: opts.clockToleranceSec ?? DEFAULT_CLOCK_TOLERANCE_SEC,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
let payload: JWTPayload;
|
|
141
|
+
try {
|
|
142
|
+
const result = await jwtVerify(opts.idToken, key, verifyOpts);
|
|
143
|
+
payload = result.payload;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
throw classifyVerifyError(err);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const nonceClaim = payload.nonce;
|
|
149
|
+
if (typeof nonceClaim !== "string" || nonceClaim.length === 0) {
|
|
150
|
+
throw new AuthenticationError("verifyIdToken: id_token missing nonce claim");
|
|
151
|
+
}
|
|
152
|
+
if (!constantTimeStringEqual(nonceClaim, opts.expectedNonce)) {
|
|
153
|
+
throw new AuthenticationError("verifyIdToken: nonce mismatch");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return payload;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function classifyVerifyError(err: unknown): AuthenticationError {
|
|
160
|
+
if (err instanceof joseErrors.JWTExpired) {
|
|
161
|
+
return new AuthenticationError("verifyIdToken: id_token expired");
|
|
162
|
+
}
|
|
163
|
+
if (err instanceof joseErrors.JWTClaimValidationFailed) {
|
|
164
|
+
return new AuthenticationError(`verifyIdToken: claim invalid (${err.claim})`);
|
|
165
|
+
}
|
|
166
|
+
if (err instanceof joseErrors.JWSSignatureVerificationFailed) {
|
|
167
|
+
return new AuthenticationError("verifyIdToken: signature verification failed");
|
|
168
|
+
}
|
|
169
|
+
if (err instanceof joseErrors.JWKSNoMatchingKey) {
|
|
170
|
+
return new AuthenticationError("verifyIdToken: no matching JWKS key");
|
|
171
|
+
}
|
|
172
|
+
return new AuthenticationError(
|
|
173
|
+
`verifyIdToken: ${err instanceof Error ? err.message : String(err)}`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Exposed for tests; do not depend on this outside the framework. */
|
|
178
|
+
export const _internals = { jwksCache, DEFAULT_CLOCK_TOLERANCE_SEC };
|