@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,169 @@
1
+ import { randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { getCookie, setCookie } from "hono/cookie";
3
+ import { AuthorizationError } from "../errors/AuthorizationError.js";
4
+ import { checkCookieDomainAgainstPublicSuffix } from "../utils/publicSuffix.js";
5
+ /**
6
+ * `csrf({ allowedOrigins, doubleSubmit? })` — declared CSRF stance.
7
+ *
8
+ * Default mode (`doubleSubmit: false`):
9
+ * - Mutating requests (POST/PUT/PATCH/DELETE) must carry an `Origin` (or
10
+ * `Referer`) header whose origin matches one of `allowedOrigins`.
11
+ * - `Sec-Fetch-Site: cross-site` is rejected outright when the header is
12
+ * present (modern browsers; gracefully ignored on older clients).
13
+ * - This protects against the typical "user follows a link / submits a
14
+ * form on attacker.com that hits api.yours.com" scenario when paired
15
+ * with `httpOnly, secure, sameSite=lax` session/JWT cookies.
16
+ *
17
+ * Hardened mode (`doubleSubmit: true`):
18
+ * - Issues a `csrfToken` cookie on safe (GET/HEAD/OPTIONS) requests when
19
+ * none is present.
20
+ * - On mutating requests, requires the client to echo the cookie value
21
+ * in the `X-CSRF-Token` header (the "double-submit cookie" pattern).
22
+ * - Cookie comparison uses `crypto.timingSafeEqual`.
23
+ * - Recommended for: third-party-iframed apps, multi-subdomain consumers
24
+ * where SameSite=lax doesn't fully protect.
25
+ *
26
+ * Documented in `.cursor/rules/security.mdc` (Why subprotocol over alternatives,
27
+ * and the CSRF stance).
28
+ */
29
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
30
+ const COOKIE_NAME = "ship_csrf";
31
+ const HEADER_NAME = "x-csrf-token";
32
+ /**
33
+ * Extract a canonical origin from `Origin` (preferred) or `Referer`
34
+ * (fallback). F24: both paths are validated via `new URL(...)` so a
35
+ * malformed value (`javascript:alert()`, garbage, empty-after-trim)
36
+ * never makes it to the allowlist membership check. Returned origin
37
+ * is lowercased so the allowlist comparison is case-insensitive (per
38
+ * RFC 6454 §4 hosts MUST compare ASCII-case-insensitively).
39
+ */
40
+ function originFromHeaders(c) {
41
+ const origin = c.req.header("origin");
42
+ if (origin) {
43
+ try {
44
+ const url = new URL(origin);
45
+ if (url.protocol !== "http:" && url.protocol !== "https:")
46
+ return undefined;
47
+ return `${url.protocol}//${url.host}`.toLowerCase();
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ }
53
+ const referer = c.req.header("referer");
54
+ if (!referer)
55
+ return undefined;
56
+ try {
57
+ const url = new URL(referer);
58
+ if (url.protocol !== "http:" && url.protocol !== "https:")
59
+ return undefined;
60
+ return `${url.protocol}//${url.host}`.toLowerCase();
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
65
+ }
66
+ /**
67
+ * Token length in characters. `randomBytes(24).toString('base64url')`
68
+ * always produces exactly 32 base64url characters. F25: the
69
+ * double-submit verifier checks both cookie + header against this
70
+ * length before the constant-time compare, so an attacker who can
71
+ * only forge a wrong-length value (e.g. truncated by a buggy proxy,
72
+ * or a deliberate short-circuit attempt) gets a fast rejection
73
+ * without revealing per-byte timing of the compare.
74
+ */
75
+ const TOKEN_LENGTH = 32;
76
+ function generateToken() {
77
+ return randomBytes(24).toString("base64url");
78
+ }
79
+ function safeEqual(a, b) {
80
+ const aBuf = Buffer.from(a);
81
+ const bBuf = Buffer.from(b);
82
+ if (aBuf.length !== bBuf.length) {
83
+ timingSafeEqual(aBuf, aBuf); // dummy to keep timing constant-ish
84
+ return false;
85
+ }
86
+ return timingSafeEqual(aBuf, bBuf);
87
+ }
88
+ export function csrf(opts) {
89
+ if (!opts.allowedOrigins || opts.allowedOrigins.length === 0) {
90
+ throw new Error("csrf(): `allowedOrigins` must be a non-empty list");
91
+ }
92
+ // F5: defend the consumer's double-submit cookie against public-
93
+ // suffix domain misconfiguration at middleware-construction time.
94
+ const domainFailure = checkCookieDomainAgainstPublicSuffix(opts.cookie?.domain);
95
+ if (domainFailure) {
96
+ throw new Error(`csrf(): invalid cookie.domain — ${domainFailure}`);
97
+ }
98
+ // F24: lowercase the allowlist so origin comparison is
99
+ // case-insensitive per RFC 6454 §4 (hosts compare ASCII-case-
100
+ // insensitively). Without this, `https://App.Example.com` in the
101
+ // config wouldn't match `https://app.example.com` from a browser.
102
+ const allowed = new Set(opts.allowedOrigins.map((o) => o.toLowerCase()));
103
+ const doubleSubmit = opts.doubleSubmit ?? false;
104
+ const headerOnly = opts.headerOnly ?? false;
105
+ const cookieName = opts.cookieName ?? COOKIE_NAME;
106
+ const headerName = (opts.headerName ?? HEADER_NAME).toLowerCase();
107
+ const cookieAttrs = opts.cookie ?? {};
108
+ const middleware = async (c, next) => {
109
+ const method = c.req.method.toUpperCase();
110
+ const isSafe = SAFE_METHODS.has(method);
111
+ // F27 — header-only mode skips cookie issuance. Consumer fetches
112
+ // tokens out-of-band and stores in memory only. For double-submit
113
+ // mode without `headerOnly`, ensure a token cookie exists; we
114
+ // issue on safe requests so the SPA picks it up before the first
115
+ // mutation.
116
+ if (doubleSubmit && !headerOnly && isSafe && !getCookie(c, cookieName)) {
117
+ const token = generateToken();
118
+ setCookie(c, cookieName, token, {
119
+ httpOnly: false, // client JS must read it to echo into the header
120
+ secure: cookieAttrs.secure ?? true,
121
+ sameSite: cookieAttrs.sameSite ?? "Lax",
122
+ domain: cookieAttrs.domain,
123
+ maxAge: cookieAttrs.maxAge ?? 60 * 60 * 24 * 7,
124
+ path: "/",
125
+ });
126
+ }
127
+ if (isSafe)
128
+ return next();
129
+ // Mutating request — apply Origin check.
130
+ const origin = originFromHeaders(c);
131
+ if (!origin || !allowed.has(origin)) {
132
+ throw new AuthorizationError(origin
133
+ ? `CSRF: origin "${origin}" is not allowed`
134
+ : "CSRF: missing Origin / Referer header on mutating request");
135
+ }
136
+ // Modern browsers tag cross-site initiations explicitly.
137
+ const fetchSite = c.req.header("sec-fetch-site");
138
+ if (fetchSite === "cross-site") {
139
+ throw new AuthorizationError("CSRF: Sec-Fetch-Site is cross-site");
140
+ }
141
+ // Hardened: double-submit cookie ↔ header.
142
+ if (doubleSubmit) {
143
+ const cookie = getCookie(c, cookieName);
144
+ const header = c.req.header(headerName);
145
+ if (!cookie) {
146
+ throw new AuthorizationError(`CSRF: missing ${cookieName} cookie`);
147
+ }
148
+ if (!header) {
149
+ throw new AuthorizationError(`CSRF: missing ${headerName} header`);
150
+ }
151
+ // F25: reject wrong-length values before the constant-time
152
+ // compare. An empty cookie + empty header would otherwise
153
+ // short-circuit safeEqual (both buffers length 0, equal), and
154
+ // a truncated cookie matched against a truncated header would
155
+ // pass against an attacker who controls both sides. Tokens are
156
+ // exactly TOKEN_LENGTH chars by construction; any deviation is
157
+ // either tampering or a buggy proxy.
158
+ if (cookie.length !== TOKEN_LENGTH || header.length !== TOKEN_LENGTH) {
159
+ throw new AuthorizationError("CSRF: cookie / header has wrong length");
160
+ }
161
+ if (!safeEqual(cookie, header)) {
162
+ throw new AuthorizationError("CSRF: cookie / header mismatch");
163
+ }
164
+ }
165
+ return next();
166
+ };
167
+ return middleware;
168
+ }
169
+ //# sourceMappingURL=csrf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csrf.js","sourceRoot":"","sources":["../../../src/http/middleware/csrf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ import { logger as defaultLogger } from "../../observability/logger.js";
3
+ /**
4
+ * `httpLogger()` — request-scoped logging + RequestContext binding.
5
+ *
6
+ * On each request:
7
+ * 1. Resolves a request ID (incoming `X-Request-Id` header, else a fresh
8
+ * UUID). Mirrors the value back on the response as `X-Request-Id`.
9
+ * 2. Wraps `next()` inside `runWithRequestContext({ requestId, ... })` so
10
+ * the framework-wide pino logger auto-attaches `requestId` to every
11
+ * log line emitted by downstream middleware / handlers / services /
12
+ * DrizzleEntityService lifecycle hooks. No callsite changes.
13
+ * 3. Logs one structured line per request at completion:
14
+ * `info` for 2xx/3xx/4xx, `error` for 5xx, with `{method, path, status,
15
+ * durationMs}` plus the bound `requestId`.
16
+ *
17
+ * Replaces v3's `RequestIdMiddleware` + ad-hoc console logging. Designed
18
+ * to run *first* in the middleware chain (before auth, validation, etc.)
19
+ * so every log line generated downstream gets the request ID.
20
+ */
21
+ export interface HttpLoggerOptions {
22
+ /** Override the framework default pino logger. */
23
+ logger?: typeof defaultLogger;
24
+ /** Header name to read for the incoming request ID. Default `"x-request-id"`. */
25
+ headerName?: string;
26
+ /**
27
+ * Override request-ID generation. Default is `crypto.randomUUID()`.
28
+ * Useful for tests that want deterministic IDs.
29
+ */
30
+ generateId?: () => string;
31
+ /**
32
+ * Log a "request received" line at debug level on entry. Default `false`
33
+ * because it doubles log volume; enable when you need to see timing of
34
+ * slow upstream middleware (auth, body parsing).
35
+ */
36
+ logStart?: boolean;
37
+ }
38
+ export declare function httpLogger(opts?: HttpLoggerOptions): MiddlewareHandler;
39
+ //# sourceMappingURL=httpLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpLogger.d.ts","sourceRoot":"","sources":["../../../src/http/middleware/httpLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,OAAO,EAAE,MAAM,IAAI,aAAa,EAAuB,MAAM,+BAA+B,CAAC;AAE7F;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,aAAa,CAAC;IAC9B,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAoED,wBAAgB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,iBAAiB,CAsD1E"}
@@ -0,0 +1,118 @@
1
+ import { runWithRequestContext } from "../../app/context/RequestContext.js";
2
+ import { logger as defaultLogger } from "../../observability/logger.js";
3
+ const DEFAULT_HEADER = "x-request-id";
4
+ /**
5
+ * Query-string keys whose VALUES are redacted in log emission. Case-
6
+ * insensitive match — `Token`, `TOKEN`, `token` all redact.
7
+ *
8
+ * The pino redactor (`packages/core/src/observability/logger.ts`)
9
+ * covers structured fields like `headers.authorization` and
10
+ * `body.password`, but URL query parameters arrive as a single
11
+ * string that the redactor can't see into. This list closes that
12
+ * gap defensively — consumers SHOULDN'T put secrets in query strings,
13
+ * but recovery / reset / signed-URL / share / OAuth flows commonly
14
+ * do anyway. Anything that lands here also leaks to observability
15
+ * stacks (Stackdriver, Datadog, Loki) by default.
16
+ *
17
+ * Documented in `.cursor/rules/security.mdc`. Keep in sync with the
18
+ * pino redactor's body-side list.
19
+ */
20
+ const SENSITIVE_QUERY_KEYS = new Set([
21
+ "token",
22
+ "signature",
23
+ "sig",
24
+ "key",
25
+ "secret",
26
+ "code",
27
+ "state",
28
+ "password",
29
+ "passwd",
30
+ "pwd",
31
+ "apikey",
32
+ "api_key",
33
+ "auth",
34
+ "authorization",
35
+ "access_token",
36
+ "refresh_token",
37
+ "id_token",
38
+ "client_secret",
39
+ "session",
40
+ "sessionid",
41
+ "session_id",
42
+ ]);
43
+ const REDACTED = "<redacted>";
44
+ /**
45
+ * Parse the URL's query string into a redacted key→value map. Returns
46
+ * `undefined` when there's no query, so the caller can omit the field
47
+ * entirely (cleaner log lines than `query: {}` everywhere).
48
+ */
49
+ function parseQueryRedacted(rawUrl) {
50
+ let parsed;
51
+ try {
52
+ parsed = new URL(rawUrl, "http://localhost.invalid");
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ if (parsed.search.length <= 1)
58
+ return undefined; // "" or "?"
59
+ const out = {};
60
+ for (const [k, v] of parsed.searchParams) {
61
+ out[k] = SENSITIVE_QUERY_KEYS.has(k.toLowerCase()) ? REDACTED : v;
62
+ }
63
+ if (Object.keys(out).length === 0)
64
+ return undefined;
65
+ return out;
66
+ }
67
+ export function httpLogger(opts = {}) {
68
+ const log = opts.logger ?? defaultLogger;
69
+ const headerName = (opts.headerName ?? DEFAULT_HEADER).toLowerCase();
70
+ const generateId = opts.generateId ?? (() => globalThis.crypto.randomUUID());
71
+ const middleware = async (c, next) => {
72
+ const requestId = c.req.header(headerName) ?? generateId();
73
+ c.header("X-Request-Id", requestId);
74
+ c.set("requestId", requestId);
75
+ const ctx = { requestId };
76
+ const start = performance.now();
77
+ if (opts.logStart) {
78
+ runWithRequestContext(ctx, () => {
79
+ log.debug({ method: c.req.method, path: c.req.path }, "http.request.start");
80
+ });
81
+ }
82
+ let thrown;
83
+ try {
84
+ await runWithRequestContext(ctx, async () => {
85
+ await next();
86
+ });
87
+ }
88
+ catch (err) {
89
+ thrown = err;
90
+ }
91
+ const durationMs = Math.round(performance.now() - start);
92
+ const status = c.res?.status ?? (thrown ? 500 : 200);
93
+ const query = parseQueryRedacted(c.req.url);
94
+ const fields = {
95
+ method: c.req.method,
96
+ path: c.req.path,
97
+ ...(query ? { query } : {}),
98
+ status,
99
+ durationMs,
100
+ requestId,
101
+ };
102
+ runWithRequestContext(ctx, () => {
103
+ if (thrown !== undefined) {
104
+ log.error({ ...fields, err: thrown }, "http.request.error");
105
+ }
106
+ else if (status >= 500) {
107
+ log.error(fields, "http.request");
108
+ }
109
+ else {
110
+ log.info(fields, "http.request");
111
+ }
112
+ });
113
+ if (thrown !== undefined)
114
+ throw thrown;
115
+ };
116
+ return middleware;
117
+ }
118
+ //# sourceMappingURL=httpLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpLogger.js","sourceRoot":"","sources":["../../../src/http/middleware/httpLogger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Context, MiddlewareHandler } from "hono";
2
+ import type { Cache } from "../../app/services/cache/Cache.js";
3
+ import { logger as defaultLogger } from "../../observability/logger.js";
4
+ import { AppError } from "../errors/AppError.js";
5
+ export interface StoredIdempotencyEntry {
6
+ /** Hash of the canonical request — used to detect "key reused with different payload". */
7
+ requestHash: string;
8
+ status: number;
9
+ headers: Record<string, string>;
10
+ /** Response body as text. `null` if the body was too large to cache. */
11
+ body: string | null;
12
+ bodyTooLarge?: boolean;
13
+ /** Wall-clock ms when this entry was recorded. Surfaced in replay headers. */
14
+ recordedAt: number;
15
+ }
16
+ export interface IdempotencyOptions {
17
+ /** Cache backend. Production: RedisCache. Tests: InMemoryCache. */
18
+ cache: Cache;
19
+ /** Window for replay. Stripe defaults to 24 h. */
20
+ ttlSec?: number;
21
+ /**
22
+ * Bucket namespace appended after `idempotency:`. Default `"global"`.
23
+ */
24
+ scope?: string;
25
+ /**
26
+ * Compose the actor portion of the cache key. Defaults to
27
+ * `c.get('user').id` or `'anon'`. Override for API-key-based clients.
28
+ */
29
+ keyGenerator?: (c: Context) => string;
30
+ /**
31
+ * Cap on the request/response body size that will be cached + replayed.
32
+ * Defaults to 256 KB. Larger bodies still execute the request once but
33
+ * the response isn't stored for replay (a flag is recorded so the second
34
+ * request gets a 200 with a `X-Idempotency-Replay-Lost: true` header).
35
+ */
36
+ maxBodyBytes?: number;
37
+ /** Override the framework default pino logger. */
38
+ logger?: typeof defaultLogger;
39
+ }
40
+ export declare function idempotency(opts: IdempotencyOptions): MiddlewareHandler;
41
+ /**
42
+ * Thrown when an Idempotency-Key is reused with a different request payload.
43
+ * Stripe returns 422 here; we match.
44
+ */
45
+ export declare class IdempotencyKeyReusedError extends AppError {
46
+ constructor(message?: string);
47
+ }
48
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/http/middleware/idempotency.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAiDjD,MAAM,WAAW,sBAAsB;IACrC,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,KAAK,EAAE,KAAK,CAAC;IACb,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;IACtC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,aAAa,CAAC;CAC/B;AAsCD,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAkHvE;AAED;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,QAAQ;gBACzC,OAAO,SAAgE;CAGpF"}
@@ -0,0 +1,180 @@
1
+ import { createHash } from "node:crypto";
2
+ import { logger as defaultLogger } from "../../observability/logger.js";
3
+ import { AppError } from "../errors/AppError.js";
4
+ import { ConflictError } from "../errors/ConflictError.js";
5
+ import { IDEMPOTENCY_META, setMetaTag } from "../utils/metaTags.js";
6
+ /**
7
+ * `idempotency({ cache, ttlSec, scope?, keyGenerator? })` — Stripe-style
8
+ * `Idempotency-Key` header dedup.
9
+ *
10
+ * Usage: clients send `Idempotency-Key: <uuid>` on a mutating request. If the
11
+ * server has already processed a request with that key (in the last `ttlSec`
12
+ * seconds), the previously-recorded response is replayed verbatim — same
13
+ * status, same headers, same body. Safely retryable network failures, in
14
+ * other words.
15
+ *
16
+ * Pipeline (per request bearing an Idempotency-Key):
17
+ *
18
+ * 1. Compose cache key: `idempotency:<scope>:<actor>:<idempotency-key>`.
19
+ * 2. Hash the canonical request: sha256(method + path + sortedQuery + body).
20
+ * 3. Look up the cache:
21
+ * - If a finalized entry exists for the same hash → replay it.
22
+ * - If a finalized entry exists for a *different* hash → 422
23
+ * (key reused with different payload — Stripe behavior).
24
+ * - If a "pending" sentinel exists → 409 (concurrent in-flight). The
25
+ * client should retry; the typical SDK auto-retries.
26
+ * - If absent → claim the key with a pending sentinel via
27
+ * `cache.setIfAbsent`, then run the handler.
28
+ * 4. After the handler completes (success or AppError), capture the
29
+ * response (status, headers, body) and replace the pending sentinel
30
+ * with the finalized entry.
31
+ *
32
+ * Requests without an Idempotency-Key header pass through unchanged. Apply
33
+ * this middleware to mutating routes that should be idempotent (POST that
34
+ * creates resources, payment flows, anything not safely retried otherwise).
35
+ *
36
+ * Tagged `IDEMPOTENCY_META` so `createApp` populates the route registry.
37
+ *
38
+ * Limitations (documented for adopters):
39
+ * - Response body is captured as text. Streamed / very-large bodies will
40
+ * bloat the cache; consider `maxBodyBytes` (TODO future). Default rejects
41
+ * bodies > 256 KB silently (the request runs once but isn't replayable).
42
+ * - The pending-sentinel concurrency policy returns 409 immediately
43
+ * instead of blocking. Stripe blocks; we don't, to keep the dependency
44
+ * surface narrow. Most SDKs handle 409 with retry/backoff.
45
+ */
46
+ const HEADER = "idempotency-key";
47
+ const SENTINEL_PENDING = "__pending__";
48
+ const DEFAULT_MAX_BODY_BYTES = 256 * 1024;
49
+ function defaultKeyGenerator(c) {
50
+ const user = c.get("user");
51
+ return user?.id ? `user:${user.id}` : "anon";
52
+ }
53
+ function hashRequest(method, path, query, body) {
54
+ const h = createHash("sha256");
55
+ h.update(method);
56
+ h.update("|");
57
+ h.update(path);
58
+ h.update("|");
59
+ h.update(query);
60
+ h.update("|");
61
+ h.update(body);
62
+ return h.digest("hex");
63
+ }
64
+ function sortedQuery(c) {
65
+ const url = new URL(c.req.url);
66
+ const entries = [...url.searchParams.entries()];
67
+ entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
68
+ return entries.map(([k, v]) => `${k}=${v}`).join("&");
69
+ }
70
+ async function readRequestBody(c, max) {
71
+ if (c.req.method === "GET" || c.req.method === "HEAD") {
72
+ return { text: "", tooLarge: false };
73
+ }
74
+ // Hono caches the body — calling text() multiple times is safe.
75
+ const text = await c.req.text();
76
+ return { text, tooLarge: text.length > max };
77
+ }
78
+ export function idempotency(opts) {
79
+ const ttlSec = opts.ttlSec ?? 24 * 60 * 60;
80
+ const scope = opts.scope ?? "global";
81
+ const keyGen = opts.keyGenerator ?? defaultKeyGenerator;
82
+ const maxBodyBytes = opts.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
83
+ const log = opts.logger ?? defaultLogger;
84
+ const middleware = async (c, next) => {
85
+ const idemKey = c.req.header(HEADER);
86
+ if (!idemKey)
87
+ return next();
88
+ const actor = keyGen(c);
89
+ const cacheKey = `idempotency:${scope}:${actor}:${idemKey}`;
90
+ const { text: bodyText, tooLarge: requestTooLarge } = await readRequestBody(c, maxBodyBytes);
91
+ const requestHash = hashRequest(c.req.method, c.req.path, sortedQuery(c), bodyText);
92
+ // 1. Look up existing entry
93
+ const existing = await opts.cache.get(cacheKey);
94
+ if (existing === SENTINEL_PENDING) {
95
+ // Concurrent request with same key still in flight
96
+ throw new ConflictError("Idempotent request already in flight for this Idempotency-Key. Retry after a short delay.");
97
+ }
98
+ if (existing) {
99
+ const entry = existing;
100
+ if (entry.requestHash !== requestHash) {
101
+ // Different payload under the same key — Stripe returns 422
102
+ throw new IdempotencyKeyReusedError("Idempotency-Key was reused with a different request payload");
103
+ }
104
+ // Replay
105
+ log.debug({ idemKey: idemKey.slice(0, 8) }, "idempotency.replay");
106
+ const headers = new Headers(entry.headers);
107
+ headers.set("X-Idempotency-Replay", "true");
108
+ headers.set("X-Idempotency-Recorded-At", new Date(entry.recordedAt).toISOString());
109
+ if (entry.bodyTooLarge)
110
+ headers.set("X-Idempotency-Replay-Lost", "true");
111
+ return new Response(entry.body ?? "", {
112
+ status: entry.status,
113
+ headers,
114
+ });
115
+ }
116
+ // 2. Try to claim with a pending sentinel. Use a short TTL on the
117
+ // sentinel so a crashed handler doesn't permanently block retries.
118
+ const claimed = await opts.cache.setIfAbsent(cacheKey, SENTINEL_PENDING, { ttlSec: 60 });
119
+ if (!claimed) {
120
+ // Lost the race — another request just landed. Treat as concurrent.
121
+ throw new ConflictError("Idempotent request already in flight for this Idempotency-Key. Retry after a short delay.");
122
+ }
123
+ // 3. Execute the handler. Hono's compose intercepts thrown errors and
124
+ // routes them through `app.onError`, so by the time `await next()`
125
+ // returns, c.res is the (possibly error-) response and c.error is
126
+ // the original throw if any.
127
+ try {
128
+ await next();
129
+ }
130
+ catch (err) {
131
+ // Only reached if NO error handler is registered (vanilla Hono throw).
132
+ // Drop the sentinel so a retry can run again.
133
+ await opts.cache.del(cacheKey);
134
+ throw err;
135
+ }
136
+ const downstreamErr = c.error;
137
+ // Non-AppError downstream failure: this was an unexpected exception
138
+ // (probably a code defect). Don't replay it — drop the sentinel so a
139
+ // retry can run fresh, and let Hono surface the error response normally.
140
+ if (downstreamErr !== undefined && !(downstreamErr instanceof AppError)) {
141
+ await opts.cache.del(cacheKey);
142
+ return;
143
+ }
144
+ // Capture c.res for storage. AppErrors that flowed through
145
+ // `registerErrorHandlers` produce a real Response in c.res, so this
146
+ // works for both success and AppError paths — failures can be idempotent.
147
+ const responseClone = c.res.clone();
148
+ const bodyBuf = Buffer.from(await responseClone.arrayBuffer());
149
+ const responseTooLarge = bodyBuf.length > maxBodyBytes;
150
+ const headers = {};
151
+ c.res.headers.forEach((v, k) => {
152
+ headers[k] = v;
153
+ });
154
+ const entry = {
155
+ requestHash,
156
+ status: c.res.status,
157
+ headers,
158
+ body: responseTooLarge || requestTooLarge ? null : bodyBuf.toString("utf8"),
159
+ bodyTooLarge: responseTooLarge || requestTooLarge,
160
+ recordedAt: Date.now(),
161
+ };
162
+ // Replace the pending sentinel with the finalized entry.
163
+ await opts.cache.set(cacheKey, entry, { ttlSec });
164
+ // c.res already holds the handler's response.
165
+ return undefined;
166
+ };
167
+ const tag = { ttlSec, scope };
168
+ setMetaTag(middleware, IDEMPOTENCY_META, tag);
169
+ return middleware;
170
+ }
171
+ /**
172
+ * Thrown when an Idempotency-Key is reused with a different request payload.
173
+ * Stripe returns 422 here; we match.
174
+ */
175
+ export class IdempotencyKeyReusedError extends AppError {
176
+ constructor(message = "Idempotency-Key was reused with a different request payload") {
177
+ super(message, 422, "IDEMPOTENCY_KEY_REUSED");
178
+ }
179
+ }
180
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../../src/http/middleware/idempotency.ts"],"names":[],"mappings":"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"}