@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.
Files changed (556) hide show
  1. package/CHANGELOG.md +605 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +4 -0
  4. package/README.md +58 -2
  5. package/cli/commands/add.js +537 -0
  6. package/cli/commands/contract.js +2 -0
  7. package/cli/commands/create.js +250 -0
  8. package/cli/commands/dev.js +227 -0
  9. package/cli/commands/doctor.js +286 -0
  10. package/cli/commands/generate.js +937 -0
  11. package/cli/commands/graph.js +2 -0
  12. package/cli/commands/migrate-orm.js +1171 -0
  13. package/cli/commands/update.js +37 -0
  14. package/cli/ship-api.js +285 -0
  15. package/lib/app/auth/AuthProvider.d.ts +91 -0
  16. package/lib/app/auth/AuthProvider.d.ts.map +1 -0
  17. package/lib/app/auth/AuthProvider.js +13 -0
  18. package/lib/app/auth/AuthProvider.js.map +1 -0
  19. package/lib/app/auth/adapters/firebase.d.ts +79 -0
  20. package/lib/app/auth/adapters/firebase.d.ts.map +1 -0
  21. package/lib/app/auth/adapters/firebase.js +122 -0
  22. package/lib/app/auth/adapters/firebase.js.map +1 -0
  23. package/lib/app/auth/adapters/jwtCookie.d.ts +55 -0
  24. package/lib/app/auth/adapters/jwtCookie.d.ts.map +1 -0
  25. package/lib/app/auth/adapters/jwtCookie.js +120 -0
  26. package/lib/app/auth/adapters/jwtCookie.js.map +1 -0
  27. package/lib/app/auth/index.d.ts +8 -0
  28. package/lib/app/auth/index.d.ts.map +1 -0
  29. package/lib/app/auth/index.js +7 -0
  30. package/lib/app/auth/index.js.map +1 -0
  31. package/lib/app/auth/jwt/AuthFlow.d.ts +94 -0
  32. package/lib/app/auth/jwt/AuthFlow.d.ts.map +1 -0
  33. package/lib/app/auth/jwt/AuthFlow.js +127 -0
  34. package/lib/app/auth/jwt/AuthFlow.js.map +1 -0
  35. package/lib/app/auth/jwt/DrizzleRefreshTokenStore.d.ts +80 -0
  36. package/lib/app/auth/jwt/DrizzleRefreshTokenStore.d.ts.map +1 -0
  37. package/lib/app/auth/jwt/DrizzleRefreshTokenStore.js +134 -0
  38. package/lib/app/auth/jwt/DrizzleRefreshTokenStore.js.map +1 -0
  39. package/lib/app/auth/jwt/JwtSigner.d.ts +123 -0
  40. package/lib/app/auth/jwt/JwtSigner.d.ts.map +1 -0
  41. package/lib/app/auth/jwt/JwtSigner.js +118 -0
  42. package/lib/app/auth/jwt/JwtSigner.js.map +1 -0
  43. package/lib/app/auth/jwt/RefreshTokenStore.d.ts +97 -0
  44. package/lib/app/auth/jwt/RefreshTokenStore.d.ts.map +1 -0
  45. package/lib/app/auth/jwt/RefreshTokenStore.js +122 -0
  46. package/lib/app/auth/jwt/RefreshTokenStore.js.map +1 -0
  47. package/lib/app/auth/permissionDefinition.d.ts +20 -0
  48. package/lib/app/auth/permissionDefinition.d.ts.map +1 -0
  49. package/lib/app/auth/permissionDefinition.js +2 -0
  50. package/lib/app/auth/permissionDefinition.js.map +1 -0
  51. package/lib/app/context/RequestContext.d.ts +59 -0
  52. package/lib/app/context/RequestContext.d.ts.map +1 -0
  53. package/lib/app/context/RequestContext.js +25 -0
  54. package/lib/app/context/RequestContext.js.map +1 -0
  55. package/lib/app/db/QueryOptions.d.ts +82 -0
  56. package/lib/app/db/QueryOptions.d.ts.map +1 -0
  57. package/lib/app/db/QueryOptions.js +37 -0
  58. package/lib/app/db/QueryOptions.js.map +1 -0
  59. package/lib/app/db/baseFields.d.ts +60 -0
  60. package/lib/app/db/baseFields.d.ts.map +1 -0
  61. package/lib/app/db/baseFields.js +54 -0
  62. package/lib/app/db/baseFields.js.map +1 -0
  63. package/lib/app/db/buildWhere.d.ts +22 -0
  64. package/lib/app/db/buildWhere.d.ts.map +1 -0
  65. package/lib/app/db/buildWhere.js +75 -0
  66. package/lib/app/db/buildWhere.js.map +1 -0
  67. package/lib/app/db/client.d.ts +111 -0
  68. package/lib/app/db/client.d.ts.map +1 -0
  69. package/lib/app/db/client.js +99 -0
  70. package/lib/app/db/client.js.map +1 -0
  71. package/lib/app/db/tenantScoped.d.ts +59 -0
  72. package/lib/app/db/tenantScoped.d.ts.map +1 -0
  73. package/lib/app/db/tenantScoped.js +54 -0
  74. package/lib/app/db/tenantScoped.js.map +1 -0
  75. package/lib/app/db/transaction.d.ts +58 -0
  76. package/lib/app/db/transaction.d.ts.map +1 -0
  77. package/lib/app/db/transaction.js +106 -0
  78. package/lib/app/db/transaction.js.map +1 -0
  79. package/lib/app/health/HealthCheck.d.ts +121 -0
  80. package/lib/app/health/HealthCheck.d.ts.map +1 -0
  81. package/lib/app/health/HealthCheck.js +151 -0
  82. package/lib/app/health/HealthCheck.js.map +1 -0
  83. package/lib/app/health/redisHealthCheck.d.ts +14 -0
  84. package/lib/app/health/redisHealthCheck.d.ts.map +1 -0
  85. package/lib/app/health/redisHealthCheck.js +21 -0
  86. package/lib/app/health/redisHealthCheck.js.map +1 -0
  87. package/lib/app/lifecycle/Bootstrapper.d.ts +92 -0
  88. package/lib/app/lifecycle/Bootstrapper.d.ts.map +1 -0
  89. package/lib/app/lifecycle/Bootstrapper.js +99 -0
  90. package/lib/app/lifecycle/Bootstrapper.js.map +1 -0
  91. package/lib/app/registry/routes.d.ts +89 -0
  92. package/lib/app/registry/routes.d.ts.map +1 -0
  93. package/lib/app/registry/routes.js +72 -0
  94. package/lib/app/registry/routes.js.map +1 -0
  95. package/lib/app/services/DrizzleEntityService.d.ts +123 -0
  96. package/lib/app/services/DrizzleEntityService.d.ts.map +1 -0
  97. package/lib/app/services/DrizzleEntityService.js +342 -0
  98. package/lib/app/services/DrizzleEntityService.js.map +1 -0
  99. package/lib/app/services/RedisProvider.d.ts +52 -0
  100. package/lib/app/services/RedisProvider.d.ts.map +1 -0
  101. package/lib/app/services/RedisProvider.js +75 -0
  102. package/lib/app/services/RedisProvider.js.map +1 -0
  103. package/lib/app/services/cache/Cache.d.ts +70 -0
  104. package/lib/app/services/cache/Cache.d.ts.map +1 -0
  105. package/lib/app/services/cache/Cache.js +18 -0
  106. package/lib/app/services/cache/Cache.js.map +1 -0
  107. package/lib/app/services/cache/InMemoryCache.d.ts +35 -0
  108. package/lib/app/services/cache/InMemoryCache.d.ts.map +1 -0
  109. package/lib/app/services/cache/InMemoryCache.js +76 -0
  110. package/lib/app/services/cache/InMemoryCache.js.map +1 -0
  111. package/lib/app/services/cache/RedisCache.d.ts +99 -0
  112. package/lib/app/services/cache/RedisCache.d.ts.map +1 -0
  113. package/lib/app/services/cache/RedisCache.js +150 -0
  114. package/lib/app/services/cache/RedisCache.js.map +1 -0
  115. package/lib/app/services/cache/cached.d.ts +30 -0
  116. package/lib/app/services/cache/cached.d.ts.map +1 -0
  117. package/lib/app/services/cache/cached.js +38 -0
  118. package/lib/app/services/cache/cached.js.map +1 -0
  119. package/lib/app/services/cache/index.d.ts +5 -0
  120. package/lib/app/services/cache/index.d.ts.map +1 -0
  121. package/lib/app/services/cache/index.js +4 -0
  122. package/lib/app/services/cache/index.js.map +1 -0
  123. package/lib/app/services/cron/defineCron.d.ts +57 -0
  124. package/lib/app/services/cron/defineCron.d.ts.map +1 -0
  125. package/lib/app/services/cron/defineCron.js +15 -0
  126. package/lib/app/services/cron/defineCron.js.map +1 -0
  127. package/lib/app/services/cron/index.d.ts +2 -0
  128. package/lib/app/services/cron/index.d.ts.map +1 -0
  129. package/lib/app/services/cron/index.js +2 -0
  130. package/lib/app/services/cron/index.js.map +1 -0
  131. package/lib/app/services/hooks/LifecycleHooks.d.ts +61 -0
  132. package/lib/app/services/hooks/LifecycleHooks.d.ts.map +1 -0
  133. package/lib/app/services/hooks/LifecycleHooks.js +85 -0
  134. package/lib/app/services/hooks/LifecycleHooks.js.map +1 -0
  135. package/lib/app/services/jobs/JobsManager.d.ts +67 -0
  136. package/lib/app/services/jobs/JobsManager.d.ts.map +1 -0
  137. package/lib/app/services/jobs/JobsManager.js +121 -0
  138. package/lib/app/services/jobs/JobsManager.js.map +1 -0
  139. package/lib/app/services/jobs/bullBoard.d.ts +72 -0
  140. package/lib/app/services/jobs/bullBoard.d.ts.map +1 -0
  141. package/lib/app/services/jobs/bullBoard.js +96 -0
  142. package/lib/app/services/jobs/bullBoard.js.map +1 -0
  143. package/lib/app/services/jobs/defineJob.d.ts +81 -0
  144. package/lib/app/services/jobs/defineJob.d.ts.map +1 -0
  145. package/lib/app/services/jobs/defineJob.js +49 -0
  146. package/lib/app/services/jobs/defineJob.js.map +1 -0
  147. package/lib/app/services/jobs/index.d.ts +4 -0
  148. package/lib/app/services/jobs/index.d.ts.map +1 -0
  149. package/lib/app/services/jobs/index.js +4 -0
  150. package/lib/app/services/jobs/index.js.map +1 -0
  151. package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.d.ts +23 -0
  152. package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.d.ts.map +1 -0
  153. package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.js +51 -0
  154. package/lib/app/services/rateLimit/InMemorySlidingWindowBackend.js.map +1 -0
  155. package/lib/app/services/rateLimit/RateLimitBackend.d.ts +41 -0
  156. package/lib/app/services/rateLimit/RateLimitBackend.d.ts.map +1 -0
  157. package/lib/app/services/rateLimit/RateLimitBackend.js +19 -0
  158. package/lib/app/services/rateLimit/RateLimitBackend.js.map +1 -0
  159. package/lib/app/services/rateLimit/RedisSlidingWindowBackend.d.ts +16 -0
  160. package/lib/app/services/rateLimit/RedisSlidingWindowBackend.d.ts.map +1 -0
  161. package/lib/app/services/rateLimit/RedisSlidingWindowBackend.js +80 -0
  162. package/lib/app/services/rateLimit/RedisSlidingWindowBackend.js.map +1 -0
  163. package/lib/app/services/rateLimit/index.d.ts +4 -0
  164. package/lib/app/services/rateLimit/index.d.ts.map +1 -0
  165. package/lib/app/services/rateLimit/index.js +3 -0
  166. package/lib/app/services/rateLimit/index.js.map +1 -0
  167. package/lib/app/services/storage/DiskStorageAdapter.d.ts +62 -0
  168. package/lib/app/services/storage/DiskStorageAdapter.d.ts.map +1 -0
  169. package/lib/app/services/storage/DiskStorageAdapter.js +295 -0
  170. package/lib/app/services/storage/DiskStorageAdapter.js.map +1 -0
  171. package/lib/app/services/storage/GCSStorageAdapter.d.ts +44 -0
  172. package/lib/app/services/storage/GCSStorageAdapter.d.ts.map +1 -0
  173. package/lib/app/services/storage/GCSStorageAdapter.js +133 -0
  174. package/lib/app/services/storage/GCSStorageAdapter.js.map +1 -0
  175. package/lib/app/services/storage/S3StorageAdapter.d.ts +57 -0
  176. package/lib/app/services/storage/S3StorageAdapter.d.ts.map +1 -0
  177. package/lib/app/services/storage/S3StorageAdapter.js +162 -0
  178. package/lib/app/services/storage/S3StorageAdapter.js.map +1 -0
  179. package/lib/app/services/storage/StorageService.d.ts +88 -0
  180. package/lib/app/services/storage/StorageService.d.ts.map +1 -0
  181. package/lib/app/services/storage/StorageService.js +18 -0
  182. package/lib/app/services/storage/StorageService.js.map +1 -0
  183. package/lib/app/services/storage/index.d.ts +5 -0
  184. package/lib/app/services/storage/index.d.ts.map +1 -0
  185. package/lib/app/services/storage/index.js +4 -0
  186. package/lib/app/services/storage/index.js.map +1 -0
  187. package/lib/app/utils/ContextValidators.d.ts +46 -0
  188. package/lib/app/utils/ContextValidators.d.ts.map +1 -0
  189. package/lib/app/utils/ContextValidators.js +81 -0
  190. package/lib/app/utils/ContextValidators.js.map +1 -0
  191. package/lib/app/utils/defineEnv.d.ts +80 -0
  192. package/lib/app/utils/defineEnv.d.ts.map +1 -0
  193. package/lib/app/utils/defineEnv.js +95 -0
  194. package/lib/app/utils/defineEnv.js.map +1 -0
  195. package/lib/client.d.ts +139 -0
  196. package/lib/client.d.ts.map +1 -0
  197. package/lib/client.js +98 -0
  198. package/lib/client.js.map +1 -0
  199. package/lib/contract/application.d.ts +3 -0
  200. package/lib/contract/application.d.ts.map +1 -0
  201. package/lib/contract/application.js +2 -0
  202. package/lib/contract/application.js.map +1 -0
  203. package/lib/contract/client.d.ts +3 -0
  204. package/lib/contract/client.d.ts.map +1 -0
  205. package/lib/contract/client.js +3 -0
  206. package/lib/contract/client.js.map +1 -0
  207. package/lib/contract/index.d.ts +3 -0
  208. package/lib/contract/index.d.ts.map +1 -0
  209. package/lib/contract/index.js +3 -0
  210. package/lib/contract/index.js.map +1 -0
  211. package/lib/contract/manifest.d.ts +3 -0
  212. package/lib/contract/manifest.d.ts.map +1 -0
  213. package/lib/contract/manifest.js +2 -0
  214. package/lib/contract/manifest.js.map +1 -0
  215. package/lib/contract/openapi.d.ts +3 -0
  216. package/lib/contract/openapi.d.ts.map +1 -0
  217. package/lib/contract/openapi.js +3 -0
  218. package/lib/contract/openapi.js.map +1 -0
  219. package/lib/contract/project.d.ts +3 -0
  220. package/lib/contract/project.d.ts.map +1 -0
  221. package/lib/contract/project.js +3 -0
  222. package/lib/contract/project.js.map +1 -0
  223. package/lib/contract/schema.d.ts +3 -0
  224. package/lib/contract/schema.d.ts.map +1 -0
  225. package/lib/contract/schema.js +3 -0
  226. package/lib/contract/schema.js.map +1 -0
  227. package/lib/drizzle/index.d.ts +3 -0
  228. package/lib/drizzle/index.d.ts.map +1 -0
  229. package/lib/drizzle/index.js +3 -0
  230. package/lib/drizzle/index.js.map +1 -0
  231. package/lib/hono/auth.d.ts +6 -0
  232. package/lib/hono/auth.d.ts.map +1 -0
  233. package/lib/hono/auth.js +54 -0
  234. package/lib/hono/auth.js.map +1 -0
  235. package/lib/hono/index.d.ts +4 -0
  236. package/lib/hono/index.d.ts.map +1 -0
  237. package/lib/hono/index.js +4 -0
  238. package/lib/hono/index.js.map +1 -0
  239. package/lib/http/app/createApp.d.ts +132 -0
  240. package/lib/http/app/createApp.d.ts.map +1 -0
  241. package/lib/http/app/createApp.js +215 -0
  242. package/lib/http/app/createApp.js.map +1 -0
  243. package/lib/http/app/devRoutes.d.ts +31 -0
  244. package/lib/http/app/devRoutes.d.ts.map +1 -0
  245. package/lib/http/app/devRoutes.js +145 -0
  246. package/lib/http/app/devRoutes.js.map +1 -0
  247. package/lib/http/app/errors.d.ts +45 -0
  248. package/lib/http/app/errors.d.ts.map +1 -0
  249. package/lib/http/app/errors.js +93 -0
  250. package/lib/http/app/errors.js.map +1 -0
  251. package/lib/http/app/health.d.ts +66 -0
  252. package/lib/http/app/health.d.ts.map +1 -0
  253. package/lib/http/app/health.js +104 -0
  254. package/lib/http/app/health.js.map +1 -0
  255. package/lib/http/errors/AppError.d.ts +10 -0
  256. package/lib/http/errors/AppError.d.ts.map +1 -0
  257. package/lib/http/errors/AppError.js +19 -0
  258. package/lib/http/errors/AppError.js.map +1 -0
  259. package/lib/http/errors/AuthenticationError.d.ts +8 -0
  260. package/lib/http/errors/AuthenticationError.d.ts.map +1 -0
  261. package/lib/http/errors/AuthenticationError.js +10 -0
  262. package/lib/http/errors/AuthenticationError.js.map +1 -0
  263. package/lib/http/errors/AuthorizationError.d.ts +8 -0
  264. package/lib/http/errors/AuthorizationError.d.ts.map +1 -0
  265. package/lib/http/errors/AuthorizationError.js +10 -0
  266. package/lib/http/errors/AuthorizationError.js.map +1 -0
  267. package/lib/http/errors/BadRequestError.d.ts +8 -0
  268. package/lib/http/errors/BadRequestError.d.ts.map +1 -0
  269. package/lib/http/errors/BadRequestError.js +10 -0
  270. package/lib/http/errors/BadRequestError.js.map +1 -0
  271. package/lib/http/errors/ConflictError.d.ts +8 -0
  272. package/lib/http/errors/ConflictError.d.ts.map +1 -0
  273. package/lib/http/errors/ConflictError.js +10 -0
  274. package/lib/http/errors/ConflictError.js.map +1 -0
  275. package/lib/http/errors/InternalServerError.d.ts +8 -0
  276. package/lib/http/errors/InternalServerError.d.ts.map +1 -0
  277. package/lib/http/errors/InternalServerError.js +10 -0
  278. package/lib/http/errors/InternalServerError.js.map +1 -0
  279. package/lib/http/errors/NotFoundError.d.ts +8 -0
  280. package/lib/http/errors/NotFoundError.d.ts.map +1 -0
  281. package/lib/http/errors/NotFoundError.js +10 -0
  282. package/lib/http/errors/NotFoundError.js.map +1 -0
  283. package/lib/http/errors/RateLimitError.d.ts +13 -0
  284. package/lib/http/errors/RateLimitError.d.ts.map +1 -0
  285. package/lib/http/errors/RateLimitError.js +16 -0
  286. package/lib/http/errors/RateLimitError.js.map +1 -0
  287. package/lib/http/errors/ServiceUnavailableError.d.ts +8 -0
  288. package/lib/http/errors/ServiceUnavailableError.d.ts.map +1 -0
  289. package/lib/http/errors/ServiceUnavailableError.js +10 -0
  290. package/lib/http/errors/ServiceUnavailableError.js.map +1 -0
  291. package/lib/http/errors/ValidationError.d.ts +9 -0
  292. package/lib/http/errors/ValidationError.d.ts.map +1 -0
  293. package/lib/http/errors/ValidationError.js +12 -0
  294. package/lib/http/errors/ValidationError.js.map +1 -0
  295. package/lib/http/errors/index.d.ts +11 -0
  296. package/lib/http/errors/index.d.ts.map +1 -0
  297. package/lib/http/errors/index.js +12 -0
  298. package/lib/http/errors/index.js.map +1 -0
  299. package/lib/http/middleware/apiKey.d.ts +9 -0
  300. package/lib/http/middleware/apiKey.d.ts.map +1 -0
  301. package/lib/http/middleware/apiKey.js +71 -0
  302. package/lib/http/middleware/apiKey.js.map +1 -0
  303. package/lib/http/middleware/csrf.d.ts +63 -0
  304. package/lib/http/middleware/csrf.d.ts.map +1 -0
  305. package/lib/http/middleware/csrf.js +169 -0
  306. package/lib/http/middleware/csrf.js.map +1 -0
  307. package/lib/http/middleware/httpLogger.d.ts +39 -0
  308. package/lib/http/middleware/httpLogger.d.ts.map +1 -0
  309. package/lib/http/middleware/httpLogger.js +118 -0
  310. package/lib/http/middleware/httpLogger.js.map +1 -0
  311. package/lib/http/middleware/idempotency.d.ts +48 -0
  312. package/lib/http/middleware/idempotency.d.ts.map +1 -0
  313. package/lib/http/middleware/idempotency.js +180 -0
  314. package/lib/http/middleware/idempotency.js.map +1 -0
  315. package/lib/http/middleware/rateLimit.d.ts +105 -0
  316. package/lib/http/middleware/rateLimit.d.ts.map +1 -0
  317. package/lib/http/middleware/rateLimit.js +84 -0
  318. package/lib/http/middleware/rateLimit.js.map +1 -0
  319. package/lib/http/middleware/requireAuth.d.ts +58 -0
  320. package/lib/http/middleware/requireAuth.d.ts.map +1 -0
  321. package/lib/http/middleware/requireAuth.js +63 -0
  322. package/lib/http/middleware/requireAuth.js.map +1 -0
  323. package/lib/http/middleware/requirePermission.d.ts +23 -0
  324. package/lib/http/middleware/requirePermission.d.ts.map +1 -0
  325. package/lib/http/middleware/requirePermission.js +50 -0
  326. package/lib/http/middleware/requirePermission.js.map +1 -0
  327. package/lib/http/middleware/resolveTenant.d.ts +41 -0
  328. package/lib/http/middleware/resolveTenant.d.ts.map +1 -0
  329. package/lib/http/middleware/resolveTenant.js +17 -0
  330. package/lib/http/middleware/resolveTenant.js.map +1 -0
  331. package/lib/http/streaming/sse.d.ts +31 -0
  332. package/lib/http/streaming/sse.d.ts.map +1 -0
  333. package/lib/http/streaming/sse.js +11 -0
  334. package/lib/http/streaming/sse.js.map +1 -0
  335. package/lib/http/streaming/streamLLM.d.ts +94 -0
  336. package/lib/http/streaming/streamLLM.d.ts.map +1 -0
  337. package/lib/http/streaming/streamLLM.js +94 -0
  338. package/lib/http/streaming/streamLLM.js.map +1 -0
  339. package/lib/http/utils/authCookies.d.ts +74 -0
  340. package/lib/http/utils/authCookies.d.ts.map +1 -0
  341. package/lib/http/utils/authCookies.js +102 -0
  342. package/lib/http/utils/authCookies.js.map +1 -0
  343. package/lib/http/utils/clientIp.d.ts +85 -0
  344. package/lib/http/utils/clientIp.d.ts.map +1 -0
  345. package/lib/http/utils/clientIp.js +249 -0
  346. package/lib/http/utils/clientIp.js.map +1 -0
  347. package/lib/http/utils/index.d.ts +4 -0
  348. package/lib/http/utils/index.d.ts.map +1 -0
  349. package/lib/http/utils/index.js +5 -0
  350. package/lib/http/utils/index.js.map +1 -0
  351. package/lib/http/utils/metaTags.d.ts +49 -0
  352. package/lib/http/utils/metaTags.d.ts.map +1 -0
  353. package/lib/http/utils/metaTags.js +32 -0
  354. package/lib/http/utils/metaTags.js.map +1 -0
  355. package/lib/http/utils/publicSuffix.d.ts +34 -0
  356. package/lib/http/utils/publicSuffix.d.ts.map +1 -0
  357. package/lib/http/utils/publicSuffix.js +276 -0
  358. package/lib/http/utils/publicSuffix.js.map +1 -0
  359. package/lib/http/utils/uploads.d.ts +92 -0
  360. package/lib/http/utils/uploads.d.ts.map +1 -0
  361. package/lib/http/utils/uploads.js +126 -0
  362. package/lib/http/utils/uploads.js.map +1 -0
  363. package/lib/http/utils/validate.d.ts +41 -0
  364. package/lib/http/utils/validate.d.ts.map +1 -0
  365. package/lib/http/utils/validate.js +55 -0
  366. package/lib/http/utils/validate.js.map +1 -0
  367. package/lib/http/utils/webhooks.d.ts +63 -0
  368. package/lib/http/utils/webhooks.d.ts.map +1 -0
  369. package/lib/http/utils/webhooks.js +133 -0
  370. package/lib/http/utils/webhooks.js.map +1 -0
  371. package/lib/http/ws/auth.d.ts +46 -0
  372. package/lib/http/ws/auth.d.ts.map +1 -0
  373. package/lib/http/ws/auth.js +123 -0
  374. package/lib/http/ws/auth.js.map +1 -0
  375. package/lib/index.d.ts +80 -0
  376. package/lib/index.d.ts.map +1 -0
  377. package/lib/index.js +61 -0
  378. package/lib/index.js.map +1 -0
  379. package/lib/kernel/index.d.ts +3 -0
  380. package/lib/kernel/index.d.ts.map +1 -0
  381. package/lib/kernel/index.js +3 -0
  382. package/lib/kernel/index.js.map +1 -0
  383. package/lib/libraries/.gitkeep +0 -0
  384. package/lib/libraries/ai/CHANGELOG.md +17 -0
  385. package/lib/libraries/ai/README.md +164 -0
  386. package/lib/libraries/ai/adapters/AnthropicAdapter.ts +382 -0
  387. package/lib/libraries/ai/adapters/LLMProvider.ts +76 -0
  388. package/lib/libraries/ai/adapters/OpenAIAdapter.ts +466 -0
  389. package/lib/libraries/ai/experimental/index.ts +20 -0
  390. package/lib/libraries/ai/index.ts +128 -0
  391. package/lib/libraries/ai/library.json +19 -0
  392. package/lib/libraries/ai/loop/agentLoop.ts +377 -0
  393. package/lib/libraries/ai/prompts/definePrompt.ts +0 -0
  394. package/lib/libraries/ai/rag/retrieveAndAnswer.ts +177 -0
  395. package/lib/libraries/ai/streaming/streamLLM.ts +94 -0
  396. package/lib/libraries/ai/structured/completeStructured.ts +143 -0
  397. package/lib/libraries/ai/tokens/Tokenizer.ts +216 -0
  398. package/lib/libraries/ai/tools/defineTool.ts +208 -0
  399. package/lib/libraries/ai/types.ts +260 -0
  400. package/lib/libraries/chunking/ChunkingService.ts +103 -0
  401. package/lib/libraries/chunking/README.md +104 -0
  402. package/lib/libraries/chunking/docs/api-reference.md +549 -0
  403. package/lib/libraries/chunking/docs/best-practices.md +321 -0
  404. package/lib/libraries/chunking/docs/code-organization.md +538 -0
  405. package/lib/libraries/chunking/docs/configuration.md +418 -0
  406. package/lib/libraries/chunking/docs/index.md +236 -0
  407. package/lib/libraries/chunking/docs/quick-start.md +298 -0
  408. package/lib/libraries/chunking/docs/strategies.md +407 -0
  409. package/lib/libraries/chunking/docs/summary.md +258 -0
  410. package/lib/libraries/chunking/docs/troubleshooting.md +527 -0
  411. package/lib/libraries/chunking/index.ts +17 -0
  412. package/lib/libraries/chunking/library.json +16 -0
  413. package/lib/libraries/chunking/strategies/ChunkingStrategy.ts +13 -0
  414. package/lib/libraries/chunking/strategies/FixedLengthChunkingStrategy.ts +88 -0
  415. package/lib/libraries/chunking/strategies/SemanticChunkingStrategy.ts +389 -0
  416. package/lib/libraries/chunking/strategies/SlidingWindowChunkingStrategy.ts +258 -0
  417. package/lib/libraries/chunking/types/index.ts +63 -0
  418. package/lib/libraries/email/EmailService.ts +591 -0
  419. package/lib/libraries/email/README.md +777 -0
  420. package/lib/libraries/email/TemplateRegistry.ts +148 -0
  421. package/lib/libraries/email/TemplateRenderer.ts +273 -0
  422. package/lib/libraries/email/index.ts +4 -0
  423. package/lib/libraries/email/initializeEmailService.ts +60 -0
  424. package/lib/libraries/email/library.json +12 -0
  425. package/lib/libraries/email/types/EmailAddress.ts +4 -0
  426. package/lib/libraries/email/types/EmailAttachment.ts +11 -0
  427. package/lib/libraries/email/types/EmailTag.ts +9 -0
  428. package/lib/libraries/email/types/EmailTemplate.ts +124 -0
  429. package/lib/libraries/email/types/SendBatchEmailsOptions.ts +9 -0
  430. package/lib/libraries/email/types/SendBatchEmailsResult.ts +9 -0
  431. package/lib/libraries/email/types/SendEmailOptions.ts +33 -0
  432. package/lib/libraries/email/types/SendEmailResult.ts +9 -0
  433. package/lib/libraries/email/types/index.ts +8 -0
  434. package/lib/libraries/weaviate/QueryBuilder.ts +292 -0
  435. package/lib/libraries/weaviate/README.md +284 -0
  436. package/lib/libraries/weaviate/Weaviate.ts +1419 -0
  437. package/lib/libraries/weaviate/WeaviateConfig.ts +268 -0
  438. package/lib/libraries/weaviate/docs/.gitkeep +1 -0
  439. package/lib/libraries/weaviate/docs/API_REFERENCE.md +1067 -0
  440. package/lib/libraries/weaviate/docs/CONFIGURATION.md +320 -0
  441. package/lib/libraries/weaviate/docs/ERROR_HANDLING.md +271 -0
  442. package/lib/libraries/weaviate/docs/MULTI_TENANCY.md +348 -0
  443. package/lib/libraries/weaviate/docs/PERFORMANCE.md +602 -0
  444. package/lib/libraries/weaviate/docs/PLUGIN_DEVELOPMENT.md +547 -0
  445. package/lib/libraries/weaviate/docs/QUICK_START.md +443 -0
  446. package/lib/libraries/weaviate/docs/README.md +269 -0
  447. package/lib/libraries/weaviate/docs/SCHEMA_GUIDE.md +630 -0
  448. package/lib/libraries/weaviate/docs/WEAVIATE_README.md +520 -0
  449. package/lib/libraries/weaviate/errors/WeaviateError.ts +92 -0
  450. package/lib/libraries/weaviate/errors/index.ts +1 -0
  451. package/lib/libraries/weaviate/index.ts +35 -0
  452. package/lib/libraries/weaviate/library.json +26 -0
  453. package/lib/libraries/weaviate/plugins/index.ts +2 -0
  454. package/lib/libraries/weaviate/schemas/SchemaRegistry.ts +284 -0
  455. package/lib/libraries/weaviate/schemas/index.ts +2 -0
  456. package/lib/libraries/weaviate/types/BM25SearchOptions.ts +9 -0
  457. package/lib/libraries/weaviate/types/ExtendedWeaviateClient.ts +7 -0
  458. package/lib/libraries/weaviate/types/HybridSearchOptions.ts +11 -0
  459. package/lib/libraries/weaviate/types/MultiTenancy.ts +81 -0
  460. package/lib/libraries/weaviate/types/Plugin.ts +17 -0
  461. package/lib/libraries/weaviate/types/SchemaConfig.ts +25 -0
  462. package/lib/libraries/weaviate/types/SearchMethod.ts +8 -0
  463. package/lib/libraries/weaviate/types/SearchOptions.ts +16 -0
  464. package/lib/libraries/weaviate/types/SearchRequest.ts +19 -0
  465. package/lib/libraries/weaviate/types/SearchResult.ts +16 -0
  466. package/lib/libraries/weaviate/types/SearchableField.ts +87 -0
  467. package/lib/libraries/weaviate/types/VectorIndexConfig.ts +19 -0
  468. package/lib/libraries/weaviate/types/VectorSearchOptions.ts +18 -0
  469. package/lib/libraries/weaviate/types/VectorizerConfig.ts +8 -0
  470. package/lib/libraries/weaviate/types/index.ts +29 -0
  471. package/lib/libraries/weaviate/utils/TenantContextManager.ts +94 -0
  472. package/lib/modules/accounts/index.ts +47 -0
  473. package/lib/modules/accounts/module.json +15 -0
  474. package/lib/modules/accounts/permissions.ts +33 -0
  475. package/lib/modules/accounts/routes.ts +200 -0
  476. package/lib/modules/accounts/schema.ts +219 -0
  477. package/lib/modules/accounts/services/AccountService.ts +44 -0
  478. package/lib/modules/accounts/validation/schemas.ts +70 -0
  479. package/lib/modules/oauth/adapters/GmailOAuthAdapter.ts +35 -0
  480. package/lib/modules/oauth/adapters/GoogleOAuthAdapter.ts +278 -0
  481. package/lib/modules/oauth/adapters/MicrosoftOAuthAdapter.ts +269 -0
  482. package/lib/modules/oauth/adapters/MockOAuthAdapter.ts +138 -0
  483. package/lib/modules/oauth/adapters/OAuthAdapter.ts +211 -0
  484. package/lib/modules/oauth/adapters/OAuthAdapterRegistry.ts +77 -0
  485. package/lib/modules/oauth/adapters/OutlookOAuthAdapter.ts +38 -0
  486. package/lib/modules/oauth/index.ts +79 -0
  487. package/lib/modules/oauth/module.json +23 -0
  488. package/lib/modules/oauth/permissions.ts +39 -0
  489. package/lib/modules/oauth/routes.ts +497 -0
  490. package/lib/modules/oauth/schema.ts +127 -0
  491. package/lib/modules/oauth/services/OAuthConnectionService.ts +147 -0
  492. package/lib/modules/oauth/state.ts +234 -0
  493. package/lib/modules/oauth/stateJtiCache.ts +85 -0
  494. package/lib/modules/oauth/verifyIdToken.ts +178 -0
  495. package/lib/modules/outbox/EventEmitter.ts +106 -0
  496. package/lib/modules/outbox/OutboxDrainer.ts +163 -0
  497. package/lib/modules/outbox/handlers/EventHandler.ts +115 -0
  498. package/lib/modules/outbox/index.ts +43 -0
  499. package/lib/modules/outbox/module.json +15 -0
  500. package/lib/modules/outbox/permissions.ts +36 -0
  501. package/lib/modules/outbox/routes.ts +219 -0
  502. package/lib/modules/outbox/schema.ts +207 -0
  503. package/lib/modules/outbox/services/OutboxEventService.ts +223 -0
  504. package/lib/observability/logger.d.ts +30 -0
  505. package/lib/observability/logger.d.ts.map +1 -0
  506. package/lib/observability/logger.js +110 -0
  507. package/lib/observability/logger.js.map +1 -0
  508. package/lib/observability/metrics.d.ts +3 -0
  509. package/lib/observability/metrics.d.ts.map +1 -0
  510. package/lib/observability/metrics.js +3 -0
  511. package/lib/observability/metrics.js.map +1 -0
  512. package/lib/observability/otel.d.ts +3 -0
  513. package/lib/observability/otel.d.ts.map +1 -0
  514. package/lib/observability/otel.js +3 -0
  515. package/lib/observability/otel.js.map +1 -0
  516. package/lib/observability/safeErrorFields.d.ts +14 -0
  517. package/lib/observability/safeErrorFields.d.ts.map +1 -0
  518. package/lib/observability/safeErrorFields.js +14 -0
  519. package/lib/observability/safeErrorFields.js.map +1 -0
  520. package/lib/observability/tracing.d.ts +3 -0
  521. package/lib/observability/tracing.d.ts.map +1 -0
  522. package/lib/observability/tracing.js +3 -0
  523. package/lib/observability/tracing.js.map +1 -0
  524. package/lib/package.json +4 -0
  525. package/lib/testing.d.ts +3 -0
  526. package/lib/testing.d.ts.map +1 -0
  527. package/lib/testing.js +3 -0
  528. package/lib/testing.js.map +1 -0
  529. package/package.json +193 -3
  530. package/templates/default/.claude/agents/qa-brief.md +106 -0
  531. package/templates/default/.claude/commands/sherpa-brief.md +23 -0
  532. package/templates/default/.cursor/rules/api.mdc +80 -0
  533. package/templates/default/.cursor/rules/commits.mdc +88 -0
  534. package/templates/default/.cursor/rules/security.mdc +92 -0
  535. package/templates/default/.cursor/rules/testing.mdc +123 -0
  536. package/templates/default/.cursor/rules/typescript.mdc +86 -0
  537. package/templates/default/.env.example +79 -0
  538. package/templates/default/.github/workflows/sherpa-brief.yml +180 -0
  539. package/templates/default/AGENTS.md +166 -0
  540. package/templates/default/CLAUDE.md +17 -0
  541. package/templates/default/README.md +97 -0
  542. package/templates/default/_gitignore +27 -0
  543. package/templates/default/docker-compose.yml +83 -0
  544. package/templates/default/drizzle.config.ts +34 -0
  545. package/templates/default/eslint.config.mjs +106 -0
  546. package/templates/default/package.json +57 -0
  547. package/templates/default/ship-api.config.ts +21 -0
  548. package/templates/default/src/client.ts +34 -0
  549. package/templates/default/src/config/modules.ts +23 -0
  550. package/templates/default/src/db/schema.ts +26 -0
  551. package/templates/default/src/environment.ts +81 -0
  552. package/templates/default/src/index.test.ts +53 -0
  553. package/templates/default/src/index.ts +11 -0
  554. package/templates/default/src/instrumentation.ts +13 -0
  555. package/templates/default/src/server.ts +90 -0
  556. 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 };