@lastshotlabs/bunshot 0.0.27 → 0.0.28

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 (742) hide show
  1. package/.oclif.manifest.json +39 -0
  2. package/README.md +8282 -2147
  3. package/dist/cli/commands/init.js +690 -0
  4. package/dist/cli/index.js +6 -0
  5. package/dist/cli.js +4 -4
  6. package/dist/packages/bunshot-admin/src/index.d.ts +15 -0
  7. package/dist/packages/bunshot-admin/src/index.js +11 -0
  8. package/dist/packages/bunshot-admin/src/lib/resourceTypes.d.ts +8 -0
  9. package/dist/packages/bunshot-admin/src/lib/resourceTypes.js +33 -0
  10. package/dist/packages/bunshot-admin/src/lib/typedRoute.d.ts +14 -0
  11. package/dist/packages/bunshot-admin/src/lib/typedRoute.js +17 -0
  12. package/dist/packages/bunshot-admin/src/plugin.d.ts +4 -0
  13. package/dist/packages/bunshot-admin/src/plugin.js +46 -0
  14. package/dist/packages/bunshot-admin/src/providers/auth0Access.d.ts +6 -0
  15. package/dist/packages/bunshot-admin/src/providers/auth0Access.js +32 -0
  16. package/dist/packages/bunshot-admin/src/routes/admin.d.ts +10 -0
  17. package/dist/packages/bunshot-admin/src/routes/admin.js +923 -0
  18. package/dist/packages/bunshot-admin/src/routes/mail.d.ts +6 -0
  19. package/dist/packages/bunshot-admin/src/routes/mail.js +114 -0
  20. package/dist/packages/bunshot-admin/src/routes/permissions.d.ts +8 -0
  21. package/dist/packages/bunshot-admin/src/routes/permissions.js +315 -0
  22. package/dist/packages/bunshot-admin/src/types/config.d.ts +16 -0
  23. package/dist/packages/bunshot-admin/src/types/config.js +37 -0
  24. package/dist/packages/bunshot-admin/src/types/env.d.ts +14 -0
  25. package/dist/packages/bunshot-admin/src/types/provider.d.ts +1 -0
  26. package/dist/packages/bunshot-admin/src/types/provider.js +4 -0
  27. package/dist/packages/bunshot-auth/src/adapters/memoryAuth.d.ts +66 -0
  28. package/dist/packages/bunshot-auth/src/adapters/memoryAuth.js +1063 -0
  29. package/dist/packages/bunshot-auth/src/adapters/mongoAuth.d.ts +2 -0
  30. package/dist/packages/bunshot-auth/src/adapters/mongoAuth.js +536 -0
  31. package/dist/packages/bunshot-auth/src/adapters/sqliteAuth.d.ts +88 -0
  32. package/dist/packages/bunshot-auth/src/adapters/sqliteAuth.js +1366 -0
  33. package/dist/packages/bunshot-auth/src/admin/bunshotAccess.d.ts +2 -0
  34. package/dist/packages/bunshot-auth/src/admin/bunshotAccess.js +23 -0
  35. package/dist/packages/bunshot-auth/src/admin/bunshotUsers.d.ts +5 -0
  36. package/dist/packages/bunshot-auth/src/admin/bunshotUsers.js +131 -0
  37. package/dist/packages/bunshot-auth/src/bootstrap.d.ts +38 -0
  38. package/dist/packages/bunshot-auth/src/bootstrap.js +384 -0
  39. package/dist/packages/bunshot-auth/src/config/appConfig.d.ts +3 -0
  40. package/dist/packages/bunshot-auth/src/config/appConfig.js +4 -0
  41. package/dist/packages/bunshot-auth/src/config/authConfig.d.ts +478 -0
  42. package/dist/packages/bunshot-auth/src/config/authConfig.js +46 -0
  43. package/dist/packages/bunshot-auth/src/config/configLock.d.ts +2 -0
  44. package/dist/packages/bunshot-auth/src/config/configLock.js +10 -0
  45. package/dist/packages/bunshot-auth/src/index.d.ts +25 -0
  46. package/dist/packages/bunshot-auth/src/index.js +23 -0
  47. package/dist/packages/bunshot-auth/src/infra/mongo.d.ts +15 -0
  48. package/dist/packages/bunshot-auth/src/infra/mongo.js +44 -0
  49. package/dist/packages/bunshot-auth/src/infra/queue.d.ts +14 -0
  50. package/dist/packages/bunshot-auth/src/infra/queue.js +27 -0
  51. package/dist/packages/bunshot-auth/src/infra/redis.d.ts +5 -0
  52. package/dist/packages/bunshot-auth/src/infra/redis.js +15 -0
  53. package/dist/packages/bunshot-auth/src/infra/signing.d.ts +7 -0
  54. package/dist/packages/bunshot-auth/src/infra/signing.js +8 -0
  55. package/dist/packages/bunshot-auth/src/lib/accountLockout.d.ts +34 -0
  56. package/dist/packages/bunshot-auth/src/lib/accountLockout.js +244 -0
  57. package/dist/packages/bunshot-auth/src/lib/adapterTiers.d.ts +1 -0
  58. package/dist/packages/bunshot-auth/src/lib/adapterTiers.js +1 -0
  59. package/dist/packages/bunshot-auth/src/lib/authAdapter.d.ts +1 -0
  60. package/dist/packages/bunshot-auth/src/lib/authAdapter.js +1 -0
  61. package/dist/packages/bunshot-auth/src/lib/authContext.d.ts +15 -0
  62. package/dist/packages/bunshot-auth/src/lib/authContext.js +1 -0
  63. package/dist/packages/bunshot-auth/src/lib/authEventBus.d.ts +4 -0
  64. package/dist/packages/bunshot-auth/src/lib/authEventBus.js +15 -0
  65. package/dist/packages/bunshot-auth/src/lib/authRateLimit.d.ts +28 -0
  66. package/dist/packages/bunshot-auth/src/lib/authRateLimit.js +205 -0
  67. package/dist/{lib → packages/bunshot-auth/src/lib}/breachedPassword.d.ts +8 -2
  68. package/dist/{lib → packages/bunshot-auth/src/lib}/breachedPassword.js +22 -9
  69. package/dist/packages/bunshot-auth/src/lib/cache.d.ts +12 -0
  70. package/dist/packages/bunshot-auth/src/lib/cache.js +120 -0
  71. package/dist/packages/bunshot-auth/src/lib/clientIp.d.ts +4 -0
  72. package/dist/{lib → packages/bunshot-auth/src/lib}/clientIp.js +14 -7
  73. package/dist/packages/bunshot-auth/src/lib/cookieOptions.d.ts +27 -0
  74. package/dist/packages/bunshot-auth/src/lib/cookieOptions.js +33 -0
  75. package/dist/packages/bunshot-auth/src/lib/credentialStuffing.d.ts +40 -0
  76. package/dist/packages/bunshot-auth/src/lib/credentialStuffing.js +221 -0
  77. package/dist/packages/bunshot-auth/src/lib/deletionCancelToken.d.ts +19 -0
  78. package/dist/packages/bunshot-auth/src/lib/deletionCancelToken.js +148 -0
  79. package/dist/packages/bunshot-auth/src/lib/emailTemplates.d.ts +23 -0
  80. package/dist/packages/bunshot-auth/src/lib/emailTemplates.js +265 -0
  81. package/dist/packages/bunshot-auth/src/lib/emailVerification.d.ts +30 -0
  82. package/dist/packages/bunshot-auth/src/lib/emailVerification.js +200 -0
  83. package/dist/packages/bunshot-auth/src/lib/env.d.ts +1 -0
  84. package/dist/packages/bunshot-auth/src/lib/env.js +3 -0
  85. package/dist/packages/bunshot-auth/src/lib/fingerprint.js +36 -0
  86. package/dist/{lib → packages/bunshot-auth/src/lib}/groups.d.ts +15 -16
  87. package/dist/{lib → packages/bunshot-auth/src/lib}/groups.js +22 -34
  88. package/dist/packages/bunshot-auth/src/lib/jwks.d.ts +28 -0
  89. package/dist/packages/bunshot-auth/src/lib/jwks.js +79 -0
  90. package/dist/packages/bunshot-auth/src/lib/jwt.d.ts +12 -0
  91. package/dist/packages/bunshot-auth/src/lib/jwt.js +86 -0
  92. package/dist/{lib → packages/bunshot-auth/src/lib}/logger.js +3 -3
  93. package/dist/{lib → packages/bunshot-auth/src/lib}/m2m.d.ts +5 -4
  94. package/dist/{lib → packages/bunshot-auth/src/lib}/m2m.js +6 -10
  95. package/dist/packages/bunshot-auth/src/lib/magicLink.d.ts +13 -0
  96. package/dist/packages/bunshot-auth/src/lib/magicLink.js +145 -0
  97. package/dist/packages/bunshot-auth/src/lib/mfaChallenge.d.ts +60 -0
  98. package/dist/packages/bunshot-auth/src/lib/mfaChallenge.js +419 -0
  99. package/dist/packages/bunshot-auth/src/lib/oauth.d.ts +82 -0
  100. package/dist/packages/bunshot-auth/src/lib/oauth.js +177 -0
  101. package/dist/packages/bunshot-auth/src/lib/oauthCode.d.ts +19 -0
  102. package/dist/packages/bunshot-auth/src/lib/oauthCode.js +182 -0
  103. package/dist/packages/bunshot-auth/src/lib/oauthReauth.d.ts +19 -0
  104. package/dist/packages/bunshot-auth/src/lib/oauthReauth.js +255 -0
  105. package/dist/packages/bunshot-auth/src/lib/organization.d.ts +66 -0
  106. package/dist/packages/bunshot-auth/src/lib/organization.js +225 -0
  107. package/dist/packages/bunshot-auth/src/lib/passwordHistory.d.ts +12 -0
  108. package/dist/packages/bunshot-auth/src/lib/passwordHistory.js +31 -0
  109. package/dist/packages/bunshot-auth/src/lib/resetPassword.d.ts +20 -0
  110. package/dist/packages/bunshot-auth/src/lib/resetPassword.js +148 -0
  111. package/dist/packages/bunshot-auth/src/lib/roles.d.ts +9 -0
  112. package/dist/packages/bunshot-auth/src/lib/roles.js +93 -0
  113. package/dist/packages/bunshot-auth/src/lib/saml.d.ts +29 -0
  114. package/dist/packages/bunshot-auth/src/lib/saml.js +73 -0
  115. package/dist/packages/bunshot-auth/src/lib/samlRequestId.d.ts +13 -0
  116. package/dist/packages/bunshot-auth/src/lib/samlRequestId.js +129 -0
  117. package/dist/{lib → packages/bunshot-auth/src/lib}/scim.d.ts +7 -7
  118. package/dist/{lib → packages/bunshot-auth/src/lib}/scim.js +15 -13
  119. package/dist/packages/bunshot-auth/src/lib/securityEventWiring.d.ts +22 -0
  120. package/dist/packages/bunshot-auth/src/lib/securityEventWiring.js +65 -0
  121. package/dist/packages/bunshot-auth/src/lib/session.d.ts +45 -0
  122. package/dist/packages/bunshot-auth/src/lib/session.js +1211 -0
  123. package/dist/packages/bunshot-auth/src/lib/storeInfra.d.ts +26 -0
  124. package/dist/packages/bunshot-auth/src/lib/storeInfra.js +18 -0
  125. package/dist/{lib → packages/bunshot-auth/src/lib}/suspension.d.ts +3 -2
  126. package/dist/{lib → packages/bunshot-auth/src/lib}/suspension.js +2 -5
  127. package/dist/packages/bunshot-auth/src/lib/validateAdapter.d.ts +16 -0
  128. package/dist/packages/bunshot-auth/src/lib/validateAdapter.js +161 -0
  129. package/dist/packages/bunshot-auth/src/middleware/bearerAuth.d.ts +13 -0
  130. package/dist/packages/bunshot-auth/src/middleware/bearerAuth.js +58 -0
  131. package/dist/{middleware → packages/bunshot-auth/src/middleware}/csrf.d.ts +5 -4
  132. package/dist/packages/bunshot-auth/src/middleware/csrf.js +138 -0
  133. package/dist/packages/bunshot-auth/src/middleware/identify.d.ts +4 -0
  134. package/dist/packages/bunshot-auth/src/middleware/identify.js +124 -0
  135. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireMfaSetup.d.ts +2 -2
  136. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireMfaSetup.js +10 -8
  137. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireRole.d.ts +2 -2
  138. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireRole.js +20 -16
  139. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireScope.d.ts +2 -2
  140. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireScope.js +6 -6
  141. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireStepUp.d.ts +2 -2
  142. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireStepUp.js +8 -7
  143. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireVerifiedEmail.d.ts +2 -2
  144. package/dist/{middleware → packages/bunshot-auth/src/middleware}/requireVerifiedEmail.js +7 -6
  145. package/dist/packages/bunshot-auth/src/middleware/scimAuth.d.ts +8 -0
  146. package/dist/packages/bunshot-auth/src/middleware/scimAuth.js +29 -0
  147. package/dist/packages/bunshot-auth/src/middleware/userAuth.d.ts +3 -0
  148. package/dist/packages/bunshot-auth/src/middleware/userAuth.js +6 -0
  149. package/dist/{models → packages/bunshot-auth/src/models}/AuthUser.d.ts +12 -8
  150. package/dist/packages/bunshot-auth/src/models/AuthUser.js +53 -0
  151. package/dist/packages/bunshot-auth/src/models/Group.d.ts +19 -0
  152. package/dist/packages/bunshot-auth/src/models/Group.js +22 -0
  153. package/dist/{models → packages/bunshot-auth/src/models}/GroupMembership.d.ts +6 -8
  154. package/dist/packages/bunshot-auth/src/models/GroupMembership.js +19 -0
  155. package/dist/{models → packages/bunshot-auth/src/models}/M2MClient.d.ts +1 -1
  156. package/dist/{models → packages/bunshot-auth/src/models}/M2MClient.js +5 -5
  157. package/dist/packages/bunshot-auth/src/models/TenantRole.d.ts +13 -0
  158. package/dist/packages/bunshot-auth/src/models/TenantRole.js +17 -0
  159. package/dist/packages/bunshot-auth/src/plugin.d.ts +4 -0
  160. package/dist/packages/bunshot-auth/src/plugin.js +274 -0
  161. package/dist/packages/bunshot-auth/src/routes/auth.d.ts +15 -0
  162. package/dist/packages/bunshot-auth/src/routes/auth.js +1624 -0
  163. package/dist/packages/bunshot-auth/src/routes/groups.d.ts +4 -0
  164. package/dist/packages/bunshot-auth/src/routes/groups.js +481 -0
  165. package/dist/packages/bunshot-auth/src/routes/m2m.d.ts +2 -0
  166. package/dist/packages/bunshot-auth/src/routes/m2m.js +145 -0
  167. package/dist/packages/bunshot-auth/src/routes/mfa.d.ts +6 -0
  168. package/dist/packages/bunshot-auth/src/routes/mfa.js +991 -0
  169. package/dist/packages/bunshot-auth/src/routes/oauth.d.ts +3 -0
  170. package/dist/packages/bunshot-auth/src/routes/oauth.js +1727 -0
  171. package/dist/packages/bunshot-auth/src/routes/oidc.d.ts +2 -0
  172. package/dist/packages/bunshot-auth/src/routes/oidc.js +84 -0
  173. package/dist/packages/bunshot-auth/src/routes/organizations.d.ts +3 -0
  174. package/dist/packages/bunshot-auth/src/routes/organizations.js +741 -0
  175. package/dist/packages/bunshot-auth/src/routes/passkey.d.ts +2 -0
  176. package/dist/packages/bunshot-auth/src/routes/passkey.js +199 -0
  177. package/dist/packages/bunshot-auth/src/routes/saml.d.ts +2 -0
  178. package/dist/packages/bunshot-auth/src/routes/saml.js +226 -0
  179. package/dist/packages/bunshot-auth/src/routes/scim.d.ts +3 -0
  180. package/dist/packages/bunshot-auth/src/routes/scim.js +588 -0
  181. package/dist/packages/bunshot-auth/src/runtime.d.ts +52 -0
  182. package/dist/packages/bunshot-auth/src/runtime.js +11 -0
  183. package/dist/{schemas → packages/bunshot-auth/src/schemas}/auth.d.ts +4 -5
  184. package/dist/packages/bunshot-auth/src/schemas/auth.js +24 -0
  185. package/dist/packages/bunshot-auth/src/schemas/error.d.ts +10 -0
  186. package/dist/packages/bunshot-auth/src/schemas/error.js +10 -0
  187. package/dist/packages/bunshot-auth/src/schemas/success.d.ts +10 -0
  188. package/dist/packages/bunshot-auth/src/schemas/success.js +10 -0
  189. package/dist/packages/bunshot-auth/src/services/auth.d.ts +39 -0
  190. package/dist/packages/bunshot-auth/src/services/auth.js +378 -0
  191. package/dist/{services → packages/bunshot-auth/src/services}/mfa.d.ts +41 -17
  192. package/dist/{services → packages/bunshot-auth/src/services}/mfa.js +259 -183
  193. package/dist/packages/bunshot-auth/src/testing.d.ts +31 -0
  194. package/dist/packages/bunshot-auth/src/testing.js +23 -0
  195. package/dist/packages/bunshot-auth/src/types/adapter.d.ts +1 -0
  196. package/dist/packages/bunshot-auth/src/types/adapter.js +1 -0
  197. package/dist/packages/bunshot-auth/src/types/config.d.ts +152 -0
  198. package/dist/packages/bunshot-auth/src/types/config.js +179 -0
  199. package/dist/{routes → packages/bunshot-auth/src/types}/groups.d.ts +2 -3
  200. package/dist/packages/bunshot-auth/src/types/groups.js +1 -0
  201. package/dist/packages/bunshot-auth/src/types/oauthCode.d.ts +6 -0
  202. package/dist/packages/bunshot-auth/src/types/oauthCode.js +1 -0
  203. package/dist/packages/bunshot-auth/src/types/oauthReauth.d.ts +13 -0
  204. package/dist/packages/bunshot-auth/src/types/oauthReauth.js +1 -0
  205. package/dist/packages/bunshot-auth/src/types/redis.d.ts +1 -0
  206. package/dist/packages/bunshot-auth/src/types/redis.js +1 -0
  207. package/dist/packages/bunshot-auth/src/types/saml.d.ts +10 -0
  208. package/dist/packages/bunshot-auth/src/types/saml.js +1 -0
  209. package/dist/packages/bunshot-auth/src/types/session.d.ts +18 -0
  210. package/dist/packages/bunshot-auth/src/types/session.js +1 -0
  211. package/dist/packages/bunshot-auth/src/types/store.d.ts +1 -0
  212. package/dist/packages/bunshot-auth/src/types/store.js +1 -0
  213. package/dist/packages/bunshot-core/src/adminProvider.d.ts +95 -0
  214. package/dist/packages/bunshot-core/src/adminProvider.js +1 -0
  215. package/dist/packages/bunshot-core/src/auditLog.d.ts +34 -0
  216. package/dist/packages/bunshot-core/src/auditLog.js +1 -0
  217. package/dist/packages/bunshot-core/src/auth-adapter.d.ts +227 -0
  218. package/dist/packages/bunshot-core/src/auth-adapter.js +4 -0
  219. package/dist/packages/bunshot-core/src/authVariables.d.ts +14 -0
  220. package/dist/packages/bunshot-core/src/authVariables.js +4 -0
  221. package/dist/packages/bunshot-core/src/cache.d.ts +12 -0
  222. package/dist/packages/bunshot-core/src/cache.js +21 -0
  223. package/dist/{lib → packages/bunshot-core/src}/captcha.d.ts +1 -10
  224. package/dist/packages/bunshot-core/src/captcha.js +1 -0
  225. package/dist/packages/bunshot-core/src/clearRegistry.d.ts +6 -0
  226. package/dist/packages/bunshot-core/src/clearRegistry.js +17 -0
  227. package/dist/packages/bunshot-core/src/clientIp.d.ts +3 -0
  228. package/dist/packages/bunshot-core/src/clientIp.js +45 -0
  229. package/dist/packages/bunshot-core/src/configLock.d.ts +4 -0
  230. package/dist/packages/bunshot-core/src/configLock.js +7 -0
  231. package/dist/packages/bunshot-core/src/configValidation.d.ts +22 -0
  232. package/dist/packages/bunshot-core/src/configValidation.js +39 -0
  233. package/dist/packages/bunshot-core/src/constants.js +10 -0
  234. package/dist/packages/bunshot-core/src/context/bunshotContext.d.ts +232 -0
  235. package/dist/packages/bunshot-core/src/context/bunshotContext.js +1 -0
  236. package/dist/packages/bunshot-core/src/context/contextAccess.d.ts +3 -0
  237. package/dist/packages/bunshot-core/src/context/contextAccess.js +16 -0
  238. package/dist/packages/bunshot-core/src/context/contextStore.d.ts +16 -0
  239. package/dist/packages/bunshot-core/src/context/contextStore.js +31 -0
  240. package/dist/packages/bunshot-core/src/context/frameworkConfig.d.ts +38 -0
  241. package/dist/packages/bunshot-core/src/context/frameworkConfig.js +1 -0
  242. package/dist/packages/bunshot-core/src/context/index.d.ts +4 -0
  243. package/dist/packages/bunshot-core/src/context/index.js +2 -0
  244. package/dist/packages/bunshot-core/src/context.d.ts +40 -0
  245. package/dist/packages/bunshot-core/src/context.js +35 -0
  246. package/dist/packages/bunshot-core/src/coreContracts.d.ts +47 -0
  247. package/dist/packages/bunshot-core/src/coreContracts.js +1 -0
  248. package/dist/packages/bunshot-core/src/coreRegistrar.d.ts +6 -0
  249. package/dist/packages/bunshot-core/src/coreRegistrar.js +42 -0
  250. package/dist/{lib → packages/bunshot-core/src}/createRoute.d.ts +4 -30
  251. package/dist/{lib → packages/bunshot-core/src}/createRoute.js +39 -88
  252. package/dist/packages/bunshot-core/src/cronRegistry.d.ts +11 -0
  253. package/dist/packages/bunshot-core/src/cronRegistry.js +1 -0
  254. package/dist/packages/bunshot-core/src/crypto.d.ts +43 -0
  255. package/dist/packages/bunshot-core/src/crypto.js +74 -0
  256. package/dist/packages/bunshot-core/src/csrf.d.ts +8 -0
  257. package/dist/packages/bunshot-core/src/csrf.js +1 -0
  258. package/dist/packages/bunshot-core/src/defaults/defaultFingerprint.d.ts +7 -0
  259. package/dist/packages/bunshot-core/src/defaults/defaultFingerprint.js +19 -0
  260. package/dist/packages/bunshot-core/src/defaults/memoryCacheAdapter.d.ts +6 -0
  261. package/dist/packages/bunshot-core/src/defaults/memoryCacheAdapter.js +40 -0
  262. package/dist/packages/bunshot-core/src/defaults/memoryRateLimit.d.ts +6 -0
  263. package/dist/packages/bunshot-core/src/defaults/memoryRateLimit.js +24 -0
  264. package/dist/packages/bunshot-core/src/emailTemplates.d.ts +5 -0
  265. package/dist/packages/bunshot-core/src/emailTemplates.js +10 -0
  266. package/dist/{lib/HttpError.d.ts → packages/bunshot-core/src/errors.d.ts} +4 -1
  267. package/dist/{lib/HttpError.js → packages/bunshot-core/src/errors.js} +7 -1
  268. package/dist/packages/bunshot-core/src/eventBus.d.ts +270 -0
  269. package/dist/packages/bunshot-core/src/eventBus.js +143 -0
  270. package/dist/packages/bunshot-core/src/idempotency.d.ts +18 -0
  271. package/dist/packages/bunshot-core/src/idempotency.js +1 -0
  272. package/dist/packages/bunshot-core/src/index.d.ts +60 -0
  273. package/dist/packages/bunshot-core/src/index.js +34 -0
  274. package/dist/packages/bunshot-core/src/mail.d.ts +14 -0
  275. package/dist/packages/bunshot-core/src/mail.js +8 -0
  276. package/dist/packages/bunshot-core/src/memoryEviction.d.ts +24 -0
  277. package/dist/packages/bunshot-core/src/memoryEviction.js +52 -0
  278. package/dist/packages/bunshot-core/src/pagination.d.ts +45 -0
  279. package/dist/packages/bunshot-core/src/pagination.js +61 -0
  280. package/dist/packages/bunshot-core/src/permissions.d.ts +64 -0
  281. package/dist/packages/bunshot-core/src/permissions.js +27 -0
  282. package/dist/packages/bunshot-core/src/plugin.d.ts +44 -0
  283. package/dist/packages/bunshot-core/src/plugin.js +1 -0
  284. package/dist/packages/bunshot-core/src/rateLimit.d.ts +5 -0
  285. package/dist/packages/bunshot-core/src/rateLimit.js +18 -0
  286. package/dist/packages/bunshot-core/src/redis.d.ts +21 -0
  287. package/dist/packages/bunshot-core/src/redis.js +1 -0
  288. package/dist/packages/bunshot-core/src/routeAuth.d.ts +5 -0
  289. package/dist/packages/bunshot-core/src/routeAuth.js +11 -0
  290. package/dist/packages/bunshot-core/src/routeOverrides.d.ts +24 -0
  291. package/dist/packages/bunshot-core/src/routeOverrides.js +25 -0
  292. package/dist/packages/bunshot-core/src/routerAdapter.d.ts +6 -0
  293. package/dist/packages/bunshot-core/src/routerAdapter.js +56 -0
  294. package/dist/packages/bunshot-core/src/secrets.d.ts +48 -0
  295. package/dist/packages/bunshot-core/src/secrets.js +8 -0
  296. package/dist/packages/bunshot-core/src/signing.d.ts +41 -0
  297. package/dist/packages/bunshot-core/src/signing.js +1 -0
  298. package/dist/packages/bunshot-core/src/sse.d.ts +36 -0
  299. package/dist/packages/bunshot-core/src/sse.js +1 -0
  300. package/dist/packages/bunshot-core/src/storageAdapter.js +1 -0
  301. package/dist/packages/bunshot-core/src/storeInfra.d.ts +44 -0
  302. package/dist/packages/bunshot-core/src/storeInfra.js +18 -0
  303. package/dist/packages/bunshot-core/src/storeType.d.ts +7 -0
  304. package/dist/packages/bunshot-core/src/storeType.js +1 -0
  305. package/dist/packages/bunshot-core/src/testing.d.ts +1 -0
  306. package/dist/packages/bunshot-core/src/testing.js +1 -0
  307. package/dist/packages/bunshot-core/src/uploadRegistry.d.ts +23 -0
  308. package/dist/packages/bunshot-core/src/uploadRegistry.js +4 -0
  309. package/dist/packages/bunshot-core/src/userResolver.d.ts +5 -0
  310. package/dist/packages/bunshot-core/src/userResolver.js +14 -0
  311. package/dist/packages/bunshot-core/src/wsMessages.d.ts +42 -0
  312. package/dist/packages/bunshot-core/src/wsMessages.js +4 -0
  313. package/dist/packages/bunshot-permissions/src/adapters/memory.d.ts +7 -0
  314. package/dist/packages/bunshot-permissions/src/adapters/memory.js +73 -0
  315. package/dist/packages/bunshot-permissions/src/index.d.ts +10 -0
  316. package/dist/packages/bunshot-permissions/src/index.js +5 -0
  317. package/dist/packages/bunshot-permissions/src/lib/bootstrap.d.ts +7 -0
  318. package/dist/packages/bunshot-permissions/src/lib/bootstrap.js +12 -0
  319. package/dist/packages/bunshot-permissions/src/lib/evaluator.d.ts +10 -0
  320. package/dist/packages/bunshot-permissions/src/lib/evaluator.js +165 -0
  321. package/dist/packages/bunshot-permissions/src/lib/registry.d.ts +2 -0
  322. package/dist/packages/bunshot-permissions/src/lib/registry.js +31 -0
  323. package/dist/packages/bunshot-permissions/src/lib/validation.d.ts +1 -0
  324. package/dist/packages/bunshot-permissions/src/lib/validation.js +1 -0
  325. package/dist/packages/bunshot-permissions/src/types/adapter.d.ts +1 -0
  326. package/dist/packages/bunshot-permissions/src/types/adapter.js +1 -0
  327. package/dist/packages/bunshot-permissions/src/types/evaluator.d.ts +1 -0
  328. package/dist/packages/bunshot-permissions/src/types/evaluator.js +1 -0
  329. package/dist/packages/bunshot-permissions/src/types/models.d.ts +1 -0
  330. package/dist/packages/bunshot-permissions/src/types/models.js +1 -0
  331. package/dist/packages/bunshot-permissions/src/types/registry.d.ts +1 -0
  332. package/dist/packages/bunshot-permissions/src/types/registry.js +1 -0
  333. package/dist/packages/bunshot-postgres/src/adapter.d.ts +6 -0
  334. package/dist/packages/bunshot-postgres/src/adapter.js +794 -0
  335. package/dist/packages/bunshot-postgres/src/connection.d.ts +15 -0
  336. package/dist/packages/bunshot-postgres/src/connection.js +16 -0
  337. package/dist/packages/bunshot-postgres/src/index.d.ts +4 -0
  338. package/dist/packages/bunshot-postgres/src/index.js +2 -0
  339. package/dist/packages/bunshot-postgres/src/schema.d.ts +997 -0
  340. package/dist/packages/bunshot-postgres/src/schema.js +105 -0
  341. package/dist/src/app.d.ts +230 -0
  342. package/dist/src/app.js +182 -0
  343. package/dist/src/cli/commands/init.d.ts +10 -0
  344. package/dist/src/cli/commands/init.js +709 -0
  345. package/dist/src/cli/index.d.ts +1 -0
  346. package/dist/src/cli/index.js +3 -0
  347. package/dist/src/entrypoints/mongo.d.ts +6 -0
  348. package/dist/src/entrypoints/mongo.js +4 -0
  349. package/dist/src/entrypoints/queue.d.ts +2 -0
  350. package/dist/src/entrypoints/queue.js +1 -0
  351. package/dist/src/entrypoints/redis.d.ts +1 -0
  352. package/dist/src/entrypoints/redis.js +1 -0
  353. package/dist/{adapters → src/framework/adapters}/localStorage.d.ts +1 -1
  354. package/dist/{adapters → src/framework/adapters}/localStorage.js +10 -10
  355. package/dist/src/framework/adapters/memoryStorage.d.ts +2 -0
  356. package/dist/src/framework/adapters/memoryStorage.js +45 -0
  357. package/dist/{adapters → src/framework/adapters}/s3Storage.d.ts +1 -1
  358. package/dist/{adapters → src/framework/adapters}/s3Storage.js +12 -12
  359. package/dist/src/framework/admin/bunshotAccess.d.ts +2 -0
  360. package/dist/src/framework/admin/bunshotAccess.js +23 -0
  361. package/dist/src/framework/admin/bunshotUsers.d.ts +2 -0
  362. package/dist/src/framework/admin/bunshotUsers.js +103 -0
  363. package/dist/src/framework/admin/index.d.ts +7 -0
  364. package/dist/src/framework/admin/index.js +21 -0
  365. package/dist/src/framework/boundaryAdapters/cacheFactories.d.ts +13 -0
  366. package/dist/src/framework/boundaryAdapters/cacheFactories.js +86 -0
  367. package/dist/src/framework/boundaryAdapters/index.d.ts +2 -0
  368. package/dist/src/framework/boundaryAdapters/index.js +1 -0
  369. package/dist/src/framework/boundaryAdapters.d.ts +17 -0
  370. package/dist/src/framework/boundaryAdapters.js +62 -0
  371. package/dist/src/framework/buildContext.d.ts +33 -0
  372. package/dist/src/framework/buildContext.js +119 -0
  373. package/dist/src/framework/config/schema.d.ts +447 -0
  374. package/dist/src/framework/config/schema.js +528 -0
  375. package/dist/src/framework/createInfrastructure.d.ts +76 -0
  376. package/dist/src/framework/createInfrastructure.js +221 -0
  377. package/dist/src/framework/lib/auditLog.d.ts +23 -0
  378. package/dist/src/framework/lib/auditLog.js +416 -0
  379. package/dist/src/framework/lib/captcha.d.ts +11 -0
  380. package/dist/{lib → src/framework/lib}/captcha.js +13 -10
  381. package/dist/{lib → src/framework/lib}/createDtoMapper.js +4 -4
  382. package/dist/src/framework/lib/createRoute.d.ts +1 -0
  383. package/dist/src/framework/lib/createRoute.js +2 -0
  384. package/dist/{lib → src/framework/lib}/idempotency.d.ts +2 -6
  385. package/dist/src/framework/lib/idempotency.js +74 -0
  386. package/dist/src/framework/lib/logger.d.ts +3 -0
  387. package/dist/src/framework/lib/logger.js +14 -0
  388. package/dist/src/framework/lib/metrics.d.ts +34 -0
  389. package/dist/{lib → src/framework/lib}/metrics.js +49 -57
  390. package/dist/src/framework/lib/pagination.d.ts +42 -0
  391. package/dist/src/framework/lib/pagination.js +51 -0
  392. package/dist/src/framework/lib/redisTransport.d.ts +38 -0
  393. package/dist/src/framework/lib/redisTransport.js +107 -0
  394. package/dist/src/framework/lib/resolveUserId.d.ts +2 -0
  395. package/dist/src/framework/lib/resolveUserId.js +5 -0
  396. package/dist/src/framework/lib/sseCollision.d.ts +6 -0
  397. package/dist/src/framework/lib/sseCollision.js +26 -0
  398. package/dist/src/framework/lib/storageAdapter.d.ts +1 -0
  399. package/dist/src/framework/lib/storageAdapter.js +1 -0
  400. package/dist/{lib → src/framework/lib}/stripUnreferencedSchemas.js +4 -4
  401. package/dist/src/framework/lib/tenant.d.ts +21 -0
  402. package/dist/src/framework/lib/tenant.js +70 -0
  403. package/dist/{lib → src/framework/lib}/upload.d.ts +11 -10
  404. package/dist/src/framework/lib/upload.js +132 -0
  405. package/dist/src/framework/lib/uploadRegistry.d.ts +23 -0
  406. package/dist/src/framework/lib/uploadRegistry.js +34 -0
  407. package/dist/{lib → src/framework/lib}/validate.d.ts +1 -1
  408. package/dist/{lib → src/framework/lib}/validate.js +2 -2
  409. package/dist/src/framework/lib/ws.d.ts +19 -0
  410. package/dist/src/framework/lib/ws.js +130 -0
  411. package/dist/src/framework/lib/wsHeartbeat.d.ts +12 -0
  412. package/dist/src/framework/lib/wsHeartbeat.js +53 -0
  413. package/dist/src/framework/lib/wsMessages.d.ts +25 -0
  414. package/dist/src/framework/lib/wsMessages.js +45 -0
  415. package/dist/src/framework/lib/wsNamespace.d.ts +17 -0
  416. package/dist/src/framework/lib/wsNamespace.js +19 -0
  417. package/dist/src/framework/lib/wsPresence.d.ts +17 -0
  418. package/dist/src/framework/lib/wsPresence.js +84 -0
  419. package/dist/src/framework/lib/wsTransport.d.ts +38 -0
  420. package/dist/src/framework/lib/wsTransport.js +9 -0
  421. package/dist/{lib → src/framework/lib}/zodToMongoose.d.ts +1 -1
  422. package/dist/{lib → src/framework/lib}/zodToMongoose.js +11 -11
  423. package/dist/{middleware → src/framework/middleware}/auditLog.d.ts +4 -3
  424. package/dist/src/framework/middleware/auditLog.js +42 -0
  425. package/dist/{middleware → src/framework/middleware}/botProtection.d.ts +2 -2
  426. package/dist/{middleware → src/framework/middleware}/botProtection.js +8 -9
  427. package/dist/src/framework/middleware/cacheResponse.d.ts +35 -0
  428. package/dist/src/framework/middleware/cacheResponse.js +126 -0
  429. package/dist/{middleware → src/framework/middleware}/captcha.d.ts +2 -3
  430. package/dist/src/framework/middleware/captcha.js +37 -0
  431. package/dist/{middleware → src/framework/middleware}/errorHandler.d.ts +1 -1
  432. package/dist/{middleware → src/framework/middleware}/errorHandler.js +2 -2
  433. package/dist/src/framework/middleware/index.js +1 -0
  434. package/dist/{middleware → src/framework/middleware}/logger.d.ts +1 -1
  435. package/dist/src/framework/middleware/metrics.d.ts +12 -0
  436. package/dist/src/framework/middleware/metrics.js +26 -0
  437. package/dist/{middleware → src/framework/middleware}/rateLimit.d.ts +2 -2
  438. package/dist/src/framework/middleware/rateLimit.js +22 -0
  439. package/dist/src/framework/middleware/requestId.d.ts +3 -0
  440. package/dist/{middleware → src/framework/middleware}/requestId.js +2 -2
  441. package/dist/{middleware → src/framework/middleware}/requestLogger.d.ts +3 -3
  442. package/dist/{middleware → src/framework/middleware}/requestLogger.js +17 -12
  443. package/dist/{middleware → src/framework/middleware}/requestSigning.d.ts +2 -2
  444. package/dist/{middleware → src/framework/middleware}/requestSigning.js +18 -20
  445. package/dist/src/framework/middleware/tenant.d.ts +14 -0
  446. package/dist/{middleware → src/framework/middleware}/tenant.js +31 -27
  447. package/dist/src/framework/middleware/upload.d.ts +5 -0
  448. package/dist/{middleware → src/framework/middleware}/upload.js +4 -4
  449. package/dist/{middleware → src/framework/middleware}/webhookAuth.d.ts +3 -3
  450. package/dist/{middleware → src/framework/middleware}/webhookAuth.js +11 -12
  451. package/dist/src/framework/models/AuditLog.d.ts +21 -0
  452. package/dist/src/framework/models/AuditLog.js +31 -0
  453. package/dist/src/framework/mountMiddleware.d.ts +91 -0
  454. package/dist/src/framework/mountMiddleware.js +128 -0
  455. package/dist/src/framework/mountOptionalEndpoints.d.ts +103 -0
  456. package/dist/src/framework/mountOptionalEndpoints.js +47 -0
  457. package/dist/src/framework/mountRoutes.d.ts +21 -0
  458. package/dist/src/framework/mountRoutes.js +144 -0
  459. package/dist/src/framework/persistence/cronRegistry.d.ts +28 -0
  460. package/dist/src/framework/persistence/cronRegistry.js +139 -0
  461. package/dist/src/framework/persistence/idempotency.d.ts +26 -0
  462. package/dist/src/framework/persistence/idempotency.js +178 -0
  463. package/dist/src/framework/persistence/index.d.ts +6 -0
  464. package/dist/src/framework/persistence/index.js +8 -0
  465. package/dist/src/framework/persistence/storeInfra.d.ts +9 -0
  466. package/dist/src/framework/persistence/storeInfra.js +1 -0
  467. package/dist/src/framework/persistence/uploadRegistry.d.ts +35 -0
  468. package/dist/src/framework/persistence/uploadRegistry.js +235 -0
  469. package/dist/src/framework/persistence/wsMessages.d.ts +22 -0
  470. package/dist/src/framework/persistence/wsMessages.js +296 -0
  471. package/dist/src/framework/preloadSchemas.d.ts +24 -0
  472. package/dist/src/framework/preloadSchemas.js +42 -0
  473. package/dist/src/framework/registerBoundaryAdapters.d.ts +23 -0
  474. package/dist/src/framework/registerBoundaryAdapters.js +46 -0
  475. package/dist/src/framework/routes/admin.d.ts +9 -0
  476. package/dist/src/framework/routes/admin.js +361 -0
  477. package/dist/src/framework/routes/health.d.ts +1 -0
  478. package/dist/src/framework/routes/health.js +21 -0
  479. package/dist/src/framework/routes/home.d.ts +1 -0
  480. package/dist/src/framework/routes/home.js +18 -0
  481. package/dist/src/framework/routes/jobs.d.ts +3 -0
  482. package/dist/{routes → src/framework/routes}/jobs.js +128 -103
  483. package/dist/src/framework/routes/metrics.d.ts +10 -0
  484. package/dist/src/framework/routes/metrics.js +57 -0
  485. package/dist/{routes → src/framework/routes}/uploads.d.ts +3 -3
  486. package/dist/src/framework/routes/uploads.js +262 -0
  487. package/dist/src/framework/runPluginLifecycle.d.ts +27 -0
  488. package/dist/src/framework/runPluginLifecycle.js +121 -0
  489. package/dist/src/framework/secrets/frameworkSecretSchema.d.ts +58 -0
  490. package/dist/src/framework/secrets/frameworkSecretSchema.js +20 -0
  491. package/dist/src/framework/secrets/index.d.ts +9 -0
  492. package/dist/src/framework/secrets/index.js +7 -0
  493. package/dist/src/framework/secrets/providers/envProvider.d.ts +15 -0
  494. package/dist/src/framework/secrets/providers/envProvider.js +18 -0
  495. package/dist/src/framework/secrets/providers/fileProvider.d.ts +8 -0
  496. package/dist/src/framework/secrets/providers/fileProvider.js +82 -0
  497. package/dist/src/framework/secrets/providers/ssmProvider.d.ts +20 -0
  498. package/dist/src/framework/secrets/providers/ssmProvider.js +127 -0
  499. package/dist/src/framework/secrets/resolveSecretBundle.d.ts +53 -0
  500. package/dist/src/framework/secrets/resolveSecretBundle.js +84 -0
  501. package/dist/src/framework/secrets/resolveSecrets.d.ts +18 -0
  502. package/dist/src/framework/secrets/resolveSecrets.js +34 -0
  503. package/dist/src/framework/sse/index.d.ts +21 -0
  504. package/dist/src/framework/sse/index.js +109 -0
  505. package/dist/src/framework/ws/index.d.ts +11 -0
  506. package/dist/src/framework/ws/index.js +8 -0
  507. package/dist/src/index.d.ts +87 -0
  508. package/dist/src/index.js +58 -0
  509. package/dist/src/lib/appConfig.d.ts +7 -0
  510. package/dist/src/lib/appConfig.js +27 -0
  511. package/dist/src/lib/appMeta.d.ts +7 -0
  512. package/dist/src/lib/appMeta.js +3 -0
  513. package/dist/src/lib/authConfig.d.ts +532 -0
  514. package/dist/{lib/appConfig.js → src/lib/authConfig.js} +75 -17
  515. package/dist/{lib → src/lib}/context.d.ts +6 -12
  516. package/dist/{lib → src/lib}/context.js +5 -5
  517. package/dist/src/lib/logger.d.ts +1 -0
  518. package/dist/src/lib/logger.js +1 -0
  519. package/dist/src/lib/mongo.d.ts +58 -0
  520. package/dist/src/lib/mongo.js +96 -0
  521. package/dist/src/lib/queue.d.ts +72 -0
  522. package/dist/src/lib/queue.js +152 -0
  523. package/dist/src/lib/redis.d.ts +28 -0
  524. package/dist/src/lib/redis.js +72 -0
  525. package/dist/{lib → src/lib}/signing.d.ts +2 -2
  526. package/dist/src/lib/signing.js +210 -0
  527. package/dist/src/lib/signingConfig.d.ts +40 -0
  528. package/dist/src/lib/signingConfig.js +28 -0
  529. package/dist/src/server.d.ts +146 -0
  530. package/dist/src/server.js +469 -0
  531. package/dist/src/shared/lib/HttpError.d.ts +1 -0
  532. package/dist/src/shared/lib/HttpError.js +2 -0
  533. package/dist/src/shared/lib/constants.d.ts +10 -0
  534. package/dist/src/shared/lib/crypto.d.ts +43 -0
  535. package/dist/src/shared/lib/crypto.js +74 -0
  536. package/dist/src/shared/lib/signing.d.ts +52 -0
  537. package/dist/{lib → src/shared/lib}/signing.js +35 -8
  538. package/dist/src/testing.d.ts +34 -0
  539. package/dist/src/testing.js +93 -0
  540. package/package.json +60 -24
  541. package/dist/adapters/memoryAuth.d.ts +0 -52
  542. package/dist/adapters/memoryAuth.js +0 -749
  543. package/dist/adapters/memoryStorage.d.ts +0 -3
  544. package/dist/adapters/memoryStorage.js +0 -44
  545. package/dist/adapters/mongoAuth.d.ts +0 -2
  546. package/dist/adapters/mongoAuth.js +0 -403
  547. package/dist/adapters/sqliteAuth.d.ts +0 -72
  548. package/dist/adapters/sqliteAuth.js +0 -858
  549. package/dist/app.d.ts +0 -559
  550. package/dist/app.js +0 -651
  551. package/dist/entrypoints/mongo.d.ts +0 -5
  552. package/dist/entrypoints/mongo.js +0 -4
  553. package/dist/entrypoints/queue.d.ts +0 -2
  554. package/dist/entrypoints/queue.js +0 -1
  555. package/dist/entrypoints/redis.d.ts +0 -1
  556. package/dist/entrypoints/redis.js +0 -1
  557. package/dist/index.d.ts +0 -117
  558. package/dist/index.js +0 -88
  559. package/dist/lib/appConfig.d.ts +0 -275
  560. package/dist/lib/auditLog.d.ts +0 -58
  561. package/dist/lib/auditLog.js +0 -218
  562. package/dist/lib/authAdapter.d.ts +0 -246
  563. package/dist/lib/authAdapter.js +0 -7
  564. package/dist/lib/authRateLimit.d.ts +0 -13
  565. package/dist/lib/authRateLimit.js +0 -117
  566. package/dist/lib/clientIp.d.ts +0 -14
  567. package/dist/lib/credentialStuffing.d.ts +0 -31
  568. package/dist/lib/credentialStuffing.js +0 -77
  569. package/dist/lib/crypto.d.ts +0 -11
  570. package/dist/lib/crypto.js +0 -22
  571. package/dist/lib/deletionCancelToken.d.ts +0 -12
  572. package/dist/lib/deletionCancelToken.js +0 -88
  573. package/dist/lib/emailVerification.d.ts +0 -19
  574. package/dist/lib/emailVerification.js +0 -129
  575. package/dist/lib/fingerprint.js +0 -36
  576. package/dist/lib/idempotency.js +0 -182
  577. package/dist/lib/jwks.d.ts +0 -25
  578. package/dist/lib/jwks.js +0 -51
  579. package/dist/lib/jwt.d.ts +0 -15
  580. package/dist/lib/jwt.js +0 -111
  581. package/dist/lib/metrics.d.ts +0 -14
  582. package/dist/lib/mfaChallenge.d.ts +0 -55
  583. package/dist/lib/mfaChallenge.js +0 -398
  584. package/dist/lib/mongo.d.ts +0 -39
  585. package/dist/lib/mongo.js +0 -124
  586. package/dist/lib/oauth.d.ts +0 -40
  587. package/dist/lib/oauth.js +0 -101
  588. package/dist/lib/oauthCode.d.ts +0 -15
  589. package/dist/lib/oauthCode.js +0 -95
  590. package/dist/lib/pagination.d.ts +0 -119
  591. package/dist/lib/pagination.js +0 -166
  592. package/dist/lib/queue.d.ts +0 -37
  593. package/dist/lib/queue.js +0 -117
  594. package/dist/lib/redis.d.ts +0 -9
  595. package/dist/lib/redis.js +0 -61
  596. package/dist/lib/resetPassword.d.ts +0 -12
  597. package/dist/lib/resetPassword.js +0 -93
  598. package/dist/lib/roles.d.ts +0 -7
  599. package/dist/lib/roles.js +0 -49
  600. package/dist/lib/saml.d.ts +0 -25
  601. package/dist/lib/saml.js +0 -64
  602. package/dist/lib/securityEvents.d.ts +0 -28
  603. package/dist/lib/securityEvents.js +0 -26
  604. package/dist/lib/session.d.ts +0 -49
  605. package/dist/lib/session.js +0 -597
  606. package/dist/lib/tenant.d.ts +0 -15
  607. package/dist/lib/tenant.js +0 -65
  608. package/dist/lib/upload.js +0 -112
  609. package/dist/lib/uploadRegistry.d.ts +0 -18
  610. package/dist/lib/uploadRegistry.js +0 -83
  611. package/dist/lib/ws.d.ts +0 -22
  612. package/dist/lib/ws.js +0 -96
  613. package/dist/lib/wsHeartbeat.d.ts +0 -12
  614. package/dist/lib/wsHeartbeat.js +0 -57
  615. package/dist/lib/wsMessages.d.ts +0 -40
  616. package/dist/lib/wsMessages.js +0 -330
  617. package/dist/lib/wsPresence.d.ts +0 -25
  618. package/dist/lib/wsPresence.js +0 -99
  619. package/dist/middleware/auditLog.js +0 -39
  620. package/dist/middleware/bearerAuth.d.ts +0 -2
  621. package/dist/middleware/bearerAuth.js +0 -11
  622. package/dist/middleware/cacheResponse.d.ts +0 -15
  623. package/dist/middleware/cacheResponse.js +0 -178
  624. package/dist/middleware/captcha.js +0 -36
  625. package/dist/middleware/csrf.js +0 -129
  626. package/dist/middleware/identify.d.ts +0 -3
  627. package/dist/middleware/identify.js +0 -122
  628. package/dist/middleware/index.js +0 -1
  629. package/dist/middleware/metrics.d.ts +0 -9
  630. package/dist/middleware/metrics.js +0 -26
  631. package/dist/middleware/rateLimit.js +0 -22
  632. package/dist/middleware/requestId.d.ts +0 -3
  633. package/dist/middleware/scimAuth.d.ts +0 -8
  634. package/dist/middleware/scimAuth.js +0 -29
  635. package/dist/middleware/tenant.d.ts +0 -5
  636. package/dist/middleware/upload.d.ts +0 -5
  637. package/dist/middleware/userAuth.d.ts +0 -3
  638. package/dist/middleware/userAuth.js +0 -6
  639. package/dist/models/AuditLog.d.ts +0 -30
  640. package/dist/models/AuditLog.js +0 -39
  641. package/dist/models/AuthUser.js +0 -55
  642. package/dist/models/Group.d.ts +0 -21
  643. package/dist/models/Group.js +0 -28
  644. package/dist/models/GroupMembership.js +0 -25
  645. package/dist/models/TenantRole.d.ts +0 -15
  646. package/dist/models/TenantRole.js +0 -23
  647. package/dist/routes/auth.d.ts +0 -12
  648. package/dist/routes/auth.js +0 -744
  649. package/dist/routes/groups.js +0 -346
  650. package/dist/routes/health.d.ts +0 -1
  651. package/dist/routes/health.js +0 -22
  652. package/dist/routes/home.d.ts +0 -1
  653. package/dist/routes/home.js +0 -16
  654. package/dist/routes/jobs.d.ts +0 -2
  655. package/dist/routes/m2m.d.ts +0 -2
  656. package/dist/routes/m2m.js +0 -72
  657. package/dist/routes/metrics.d.ts +0 -8
  658. package/dist/routes/metrics.js +0 -55
  659. package/dist/routes/mfa.d.ts +0 -5
  660. package/dist/routes/mfa.js +0 -628
  661. package/dist/routes/oauth.d.ts +0 -2
  662. package/dist/routes/oauth.js +0 -520
  663. package/dist/routes/oidc.d.ts +0 -2
  664. package/dist/routes/oidc.js +0 -29
  665. package/dist/routes/passkey.d.ts +0 -1
  666. package/dist/routes/passkey.js +0 -157
  667. package/dist/routes/saml.d.ts +0 -2
  668. package/dist/routes/saml.js +0 -86
  669. package/dist/routes/scim.d.ts +0 -2
  670. package/dist/routes/scim.js +0 -255
  671. package/dist/routes/uploads.js +0 -227
  672. package/dist/schemas/auth.js +0 -30
  673. package/dist/server.d.ts +0 -57
  674. package/dist/server.js +0 -112
  675. package/dist/services/auth.d.ts +0 -29
  676. package/dist/services/auth.js +0 -238
  677. package/dist/ws/index.d.ts +0 -10
  678. package/dist/ws/index.js +0 -39
  679. package/docs/sections/adding-middleware/full.md +0 -35
  680. package/docs/sections/adding-models/full.md +0 -125
  681. package/docs/sections/adding-models/overview.md +0 -13
  682. package/docs/sections/adding-routes/full.md +0 -182
  683. package/docs/sections/adding-routes/overview.md +0 -23
  684. package/docs/sections/auth-flow/full.md +0 -790
  685. package/docs/sections/auth-flow/overview.md +0 -10
  686. package/docs/sections/auth-security-examples/full.md +0 -388
  687. package/docs/sections/authentication/full.md +0 -130
  688. package/docs/sections/authentication/overview.md +0 -5
  689. package/docs/sections/cli/full.md +0 -42
  690. package/docs/sections/configuration/full.md +0 -172
  691. package/docs/sections/configuration/overview.md +0 -18
  692. package/docs/sections/configuration-example/full.md +0 -117
  693. package/docs/sections/configuration-example/overview.md +0 -30
  694. package/docs/sections/documentation/full.md +0 -171
  695. package/docs/sections/environment-variables/full.md +0 -55
  696. package/docs/sections/exports/full.md +0 -123
  697. package/docs/sections/extending-context/full.md +0 -59
  698. package/docs/sections/header.md +0 -3
  699. package/docs/sections/installation/full.md +0 -6
  700. package/docs/sections/jobs/full.md +0 -140
  701. package/docs/sections/jobs/overview.md +0 -15
  702. package/docs/sections/logging/full.md +0 -83
  703. package/docs/sections/metrics/full.md +0 -131
  704. package/docs/sections/mongodb-connections/full.md +0 -45
  705. package/docs/sections/mongodb-connections/overview.md +0 -7
  706. package/docs/sections/multi-tenancy/full.md +0 -66
  707. package/docs/sections/multi-tenancy/overview.md +0 -15
  708. package/docs/sections/oauth/full.md +0 -189
  709. package/docs/sections/oauth/overview.md +0 -16
  710. package/docs/sections/package-development/full.md +0 -7
  711. package/docs/sections/pagination/full.md +0 -93
  712. package/docs/sections/passkey-login/full.md +0 -90
  713. package/docs/sections/passkey-login/overview.md +0 -1
  714. package/docs/sections/peer-dependencies/full.md +0 -47
  715. package/docs/sections/quick-start/full.md +0 -43
  716. package/docs/sections/response-caching/full.md +0 -117
  717. package/docs/sections/response-caching/overview.md +0 -13
  718. package/docs/sections/roles/full.md +0 -225
  719. package/docs/sections/roles/overview.md +0 -14
  720. package/docs/sections/running-without-redis/full.md +0 -16
  721. package/docs/sections/running-without-redis-or-mongodb/full.md +0 -60
  722. package/docs/sections/signing/full.md +0 -203
  723. package/docs/sections/stack/full.md +0 -10
  724. package/docs/sections/uploads/full.md +0 -208
  725. package/docs/sections/versioning/full.md +0 -85
  726. package/docs/sections/webhook-auth/full.md +0 -100
  727. package/docs/sections/websocket/full.md +0 -196
  728. package/docs/sections/websocket/overview.md +0 -5
  729. package/docs/sections/websocket-rooms/full.md +0 -102
  730. package/docs/sections/websocket-rooms/overview.md +0 -5
  731. /package/dist/{lib/storageAdapter.js → packages/bunshot-admin/src/types/env.js} +0 -0
  732. /package/dist/{lib → packages/bunshot-auth/src/lib}/fingerprint.d.ts +0 -0
  733. /package/dist/{lib → packages/bunshot-auth/src/lib}/logger.d.ts +0 -0
  734. /package/dist/{lib → packages/bunshot-core/src}/constants.d.ts +0 -0
  735. /package/dist/{lib → packages/bunshot-core/src}/storageAdapter.d.ts +0 -0
  736. /package/dist/{lib → src/framework/lib}/createDtoMapper.d.ts +0 -0
  737. /package/dist/{lib → src/framework/lib}/stripUnreferencedSchemas.d.ts +0 -0
  738. /package/dist/{middleware → src/framework/middleware}/cors.d.ts +0 -0
  739. /package/dist/{middleware → src/framework/middleware}/cors.js +0 -0
  740. /package/dist/{middleware → src/framework/middleware}/index.d.ts +0 -0
  741. /package/dist/{middleware → src/framework/middleware}/logger.js +0 -0
  742. /package/dist/{lib → src/shared/lib}/constants.js +0 -0
@@ -0,0 +1,1366 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { HttpError } from '../../../bunshot-core/src/index.js';
3
+ import { hashToken } from '../../../bunshot-core/src/index.js';
4
+ const MIGRATIONS = [
5
+ // v1: initial schema
6
+ db => {
7
+ db.run(`CREATE TABLE IF NOT EXISTS users (
8
+ id TEXT PRIMARY KEY,
9
+ email TEXT UNIQUE,
10
+ identifier TEXT UNIQUE,
11
+ passwordHash TEXT,
12
+ providerIds TEXT NOT NULL DEFAULT '[]',
13
+ roles TEXT NOT NULL DEFAULT '[]',
14
+ emailVerified INTEGER NOT NULL DEFAULT 0,
15
+ displayName TEXT,
16
+ firstName TEXT,
17
+ lastName TEXT,
18
+ externalId TEXT,
19
+ suspended INTEGER NOT NULL DEFAULT 0,
20
+ suspendedAt TEXT,
21
+ suspendedReason TEXT,
22
+ mfaSecret TEXT,
23
+ mfaEnabled INTEGER NOT NULL DEFAULT 0,
24
+ recoveryCodes TEXT NOT NULL DEFAULT '[]',
25
+ mfaMethods TEXT NOT NULL DEFAULT '[]',
26
+ passwordHistory TEXT NOT NULL DEFAULT '[]',
27
+ user_metadata TEXT NOT NULL DEFAULT '{}',
28
+ app_metadata TEXT NOT NULL DEFAULT '{}'
29
+ )`);
30
+ db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_users_identifier ON users(identifier) WHERE identifier IS NOT NULL');
31
+ db.run(`CREATE TABLE IF NOT EXISTS sessions (
32
+ sessionId TEXT PRIMARY KEY,
33
+ userId TEXT NOT NULL,
34
+ token TEXT,
35
+ createdAt INTEGER NOT NULL,
36
+ lastActiveAt INTEGER NOT NULL,
37
+ expiresAt INTEGER NOT NULL,
38
+ ipAddress TEXT,
39
+ userAgent TEXT,
40
+ refreshToken TEXT,
41
+ prevRefreshToken TEXT,
42
+ prevTokenExpiresAt INTEGER,
43
+ fingerprint TEXT,
44
+ mfaVerifiedAt INTEGER,
45
+ refreshTokenPlain TEXT
46
+ )`);
47
+ db.run('CREATE INDEX IF NOT EXISTS idx_sessions_userId ON sessions(userId)');
48
+ db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_refreshToken ON sessions(refreshToken) WHERE refreshToken IS NOT NULL');
49
+ db.run(`CREATE TABLE IF NOT EXISTS oauth_states (
50
+ state TEXT PRIMARY KEY,
51
+ codeVerifier TEXT,
52
+ linkUserId TEXT,
53
+ expiresAt INTEGER NOT NULL
54
+ )`);
55
+ db.run(`CREATE TABLE IF NOT EXISTS cache_entries (
56
+ key TEXT PRIMARY KEY,
57
+ value TEXT NOT NULL,
58
+ expiresAt INTEGER -- NULL = indefinite
59
+ )`);
60
+ db.run(`CREATE TABLE IF NOT EXISTS email_verifications (
61
+ token TEXT PRIMARY KEY,
62
+ userId TEXT NOT NULL,
63
+ email TEXT NOT NULL,
64
+ expiresAt INTEGER NOT NULL
65
+ )`);
66
+ db.run(`CREATE TABLE IF NOT EXISTS password_resets (
67
+ token TEXT PRIMARY KEY,
68
+ userId TEXT NOT NULL,
69
+ email TEXT NOT NULL,
70
+ expiresAt INTEGER NOT NULL
71
+ )`);
72
+ db.run(`CREATE TABLE IF NOT EXISTS tenant_roles (
73
+ userId TEXT NOT NULL,
74
+ tenantId TEXT NOT NULL,
75
+ role TEXT NOT NULL,
76
+ PRIMARY KEY (userId, tenantId, role)
77
+ )`);
78
+ db.run('CREATE INDEX IF NOT EXISTS idx_tenant_roles_tenant ON tenant_roles(tenantId)');
79
+ db.run(`CREATE TABLE IF NOT EXISTS webauthn_credentials (
80
+ credentialId TEXT PRIMARY KEY,
81
+ userId TEXT NOT NULL,
82
+ publicKey TEXT NOT NULL,
83
+ signCount INTEGER NOT NULL DEFAULT 0,
84
+ transports TEXT NOT NULL DEFAULT '[]',
85
+ name TEXT,
86
+ createdAt INTEGER NOT NULL
87
+ )`);
88
+ db.run('CREATE INDEX IF NOT EXISTS idx_webauthn_userId ON webauthn_credentials(userId)');
89
+ db.run(`CREATE TABLE IF NOT EXISTS groups (
90
+ id TEXT PRIMARY KEY,
91
+ name TEXT NOT NULL,
92
+ displayName TEXT,
93
+ description TEXT,
94
+ roles TEXT NOT NULL DEFAULT '[]',
95
+ tenantId TEXT,
96
+ createdAt INTEGER NOT NULL,
97
+ updatedAt INTEGER NOT NULL
98
+ )`);
99
+ // SQLite UNIQUE treats each NULL as distinct, so we use partial indexes instead of
100
+ // a simple UNIQUE constraint on (name, tenantId). This correctly enforces name
101
+ // uniqueness within app-wide scope and within each tenant scope separately.
102
+ db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_name_appwide ON groups(name) WHERE tenantId IS NULL');
103
+ db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_groups_name_tenant ON groups(name, tenantId) WHERE tenantId IS NOT NULL');
104
+ db.run('CREATE INDEX IF NOT EXISTS idx_groups_tenantId ON groups(tenantId)');
105
+ db.run(`CREATE TABLE IF NOT EXISTS group_memberships (
106
+ userId TEXT NOT NULL,
107
+ groupId TEXT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
108
+ roles TEXT NOT NULL DEFAULT '[]',
109
+ tenantId TEXT,
110
+ createdAt INTEGER NOT NULL,
111
+ PRIMARY KEY (userId, groupId)
112
+ )`);
113
+ // NOTE: PRAGMA foreign_keys = ON must run per-connection.
114
+ db.run('CREATE INDEX IF NOT EXISTS idx_gm_groupId ON group_memberships(groupId)');
115
+ db.run('CREATE INDEX IF NOT EXISTS idx_gm_userId_tenantId ON group_memberships(userId, tenantId)');
116
+ db.run(`CREATE TABLE IF NOT EXISTS oauth_codes (
117
+ codeHash TEXT PRIMARY KEY,
118
+ token TEXT NOT NULL,
119
+ userId TEXT NOT NULL,
120
+ email TEXT,
121
+ refreshToken TEXT,
122
+ expiresAt INTEGER NOT NULL
123
+ )`);
124
+ db.run(`CREATE TABLE IF NOT EXISTS deletion_cancel_tokens (
125
+ token TEXT PRIMARY KEY,
126
+ userId TEXT NOT NULL,
127
+ jobId TEXT NOT NULL,
128
+ expiresAt INTEGER NOT NULL
129
+ )`);
130
+ db.run(`CREATE TABLE IF NOT EXISTS magic_link_tokens (
131
+ token TEXT PRIMARY KEY,
132
+ userId TEXT NOT NULL,
133
+ expiresAt INTEGER NOT NULL
134
+ )`);
135
+ db.run(`CREATE TABLE IF NOT EXISTS oauth_reauth_states (
136
+ tokenHash TEXT PRIMARY KEY,
137
+ userId TEXT NOT NULL,
138
+ sessionId TEXT NOT NULL,
139
+ provider TEXT NOT NULL,
140
+ purpose TEXT NOT NULL,
141
+ returnUrl TEXT,
142
+ expiresAt INTEGER NOT NULL
143
+ )`);
144
+ db.run(`CREATE TABLE IF NOT EXISTS oauth_reauth_confirmations (
145
+ codeHash TEXT PRIMARY KEY,
146
+ userId TEXT NOT NULL,
147
+ purpose TEXT NOT NULL,
148
+ expiresAt INTEGER NOT NULL
149
+ )`);
150
+ db.run(`CREATE TABLE IF NOT EXISTS upload_registry (
151
+ key TEXT PRIMARY KEY,
152
+ ownerUserId TEXT,
153
+ tenantId TEXT,
154
+ mimeType TEXT,
155
+ bucket TEXT,
156
+ createdAt INTEGER NOT NULL
157
+ )`);
158
+ },
159
+ // v2: M2M clients table
160
+ db => {
161
+ db.run(`CREATE TABLE IF NOT EXISTS m2m_clients (
162
+ id TEXT PRIMARY KEY,
163
+ clientId TEXT NOT NULL UNIQUE,
164
+ clientSecretHash TEXT NOT NULL,
165
+ name TEXT NOT NULL,
166
+ scopes TEXT NOT NULL DEFAULT '[]',
167
+ active INTEGER NOT NULL DEFAULT 1,
168
+ createdAt INTEGER NOT NULL
169
+ )`);
170
+ db.run('CREATE UNIQUE INDEX IF NOT EXISTS idx_m2m_clients_clientId ON m2m_clients(clientId)');
171
+ },
172
+ // Add future migrations here. Each entry is applied exactly once to any
173
+ // database whose user_version is less than the entry's 1-based index.
174
+ // Example:
175
+ // (db) => { db.run('ALTER TABLE users ADD COLUMN phoneNumber TEXT'); },
176
+ ];
177
+ function runMigrations(db) {
178
+ const { user_version: currentVersion } = db
179
+ .query('PRAGMA user_version')
180
+ .get();
181
+ for (let i = currentVersion; i < MIGRATIONS.length; i++) {
182
+ MIGRATIONS[i](db);
183
+ // PRAGMA user_version cannot use parameter binding — i+1 is always a
184
+ // literal integer from our own code, never user input.
185
+ db.run(`PRAGMA user_version = ${i + 1}`);
186
+ }
187
+ }
188
+ function encodeSqliteCursor(createdAt, id) {
189
+ return btoa(JSON.stringify({ createdAt, id }));
190
+ }
191
+ function decodeSqliteCursor(cursor) {
192
+ try {
193
+ return JSON.parse(atob(cursor));
194
+ }
195
+ catch {
196
+ return null;
197
+ }
198
+ }
199
+ // ---------------------------------------------------------------------------
200
+ // Factory
201
+ // ---------------------------------------------------------------------------
202
+ const DEFAULT_SESSION_TTL_MS = 60 * 60 * 24 * 7 * 1000; // 7 days
203
+ export function createSqliteAuthAdapter(path) {
204
+ const db = new Database(path, { create: true });
205
+ db.run('PRAGMA journal_mode = WAL');
206
+ db.run('PRAGMA foreign_keys = ON');
207
+ runMigrations(db);
208
+ let _cleanupInterval = null;
209
+ function runCleanup(getConfig) {
210
+ const now = Date.now();
211
+ if (getConfig().persistSessionMetadata) {
212
+ // Null out tokens for expired sessions but keep the metadata row
213
+ db.run('UPDATE sessions SET token = NULL WHERE expiresAt <= ? AND token IS NOT NULL', [now]);
214
+ }
215
+ else {
216
+ db.run('DELETE FROM sessions WHERE expiresAt <= ?', [now]);
217
+ }
218
+ db.run('DELETE FROM oauth_states WHERE expiresAt <= ?', [now]);
219
+ db.run('DELETE FROM cache_entries WHERE expiresAt IS NOT NULL AND expiresAt <= ?', [now]);
220
+ db.run('DELETE FROM email_verifications WHERE expiresAt <= ?', [now]);
221
+ db.run('DELETE FROM password_resets WHERE expiresAt <= ?', [now]);
222
+ db.run('DELETE FROM oauth_codes WHERE expiresAt <= ?', [now]);
223
+ db.run('DELETE FROM oauth_reauth_states WHERE expiresAt <= ?', [now]);
224
+ db.run('DELETE FROM oauth_reauth_confirmations WHERE expiresAt <= ?', [now]);
225
+ }
226
+ function getSessionTtlMs(getConfig) {
227
+ const abs = getConfig().sessionPolicy.absoluteTimeout;
228
+ return abs ? abs * 1000 : DEFAULT_SESSION_TTL_MS;
229
+ }
230
+ // ---------------------------------------------------------------------------
231
+ // Auth adapter
232
+ // ---------------------------------------------------------------------------
233
+ const adapter = {
234
+ async findByEmail(email) {
235
+ const row = db
236
+ .query('SELECT id, passwordHash FROM users WHERE email = ?')
237
+ .get(email);
238
+ return row ?? null;
239
+ },
240
+ verifyPassword(userId, password) {
241
+ const row = db
242
+ .query('SELECT passwordHash FROM users WHERE id = ?')
243
+ .get(userId);
244
+ if (!row?.passwordHash)
245
+ return Promise.resolve(false);
246
+ return Bun.password.verify(password, row.passwordHash);
247
+ },
248
+ getIdentifier(userId) {
249
+ const row = db
250
+ .query('SELECT identifier, email FROM users WHERE id = ?')
251
+ .get(userId);
252
+ return Promise.resolve(row?.identifier ?? row?.email ?? '');
253
+ },
254
+ async create(email, passwordHash) {
255
+ const id = crypto.randomUUID();
256
+ const normalized = email.toLowerCase();
257
+ try {
258
+ db.run('INSERT INTO users (id, email, identifier, passwordHash) VALUES (?, ?, ?, ?)', [
259
+ id,
260
+ normalized,
261
+ normalized,
262
+ passwordHash,
263
+ ]);
264
+ return { id };
265
+ }
266
+ catch (err) {
267
+ if (err?.code === 'SQLITE_CONSTRAINT_UNIQUE')
268
+ throw new HttpError(409, 'Email already registered');
269
+ throw err;
270
+ }
271
+ },
272
+ async setPassword(userId, passwordHash) {
273
+ db.run('UPDATE users SET passwordHash = ? WHERE id = ?', [passwordHash, userId]);
274
+ },
275
+ async getPasswordHistory(userId) {
276
+ const row = db
277
+ .query('SELECT passwordHistory FROM users WHERE id = ?')
278
+ .get(userId);
279
+ if (!row)
280
+ return [];
281
+ try {
282
+ return JSON.parse(row.passwordHistory);
283
+ }
284
+ catch {
285
+ return [];
286
+ }
287
+ },
288
+ async addPasswordToHistory(userId, hash, maxCount) {
289
+ const row = db
290
+ .query('SELECT passwordHistory FROM users WHERE id = ?')
291
+ .get(userId);
292
+ if (!row)
293
+ return;
294
+ let history = [];
295
+ try {
296
+ history = JSON.parse(row.passwordHistory);
297
+ }
298
+ catch {
299
+ history = [];
300
+ }
301
+ history.push(hash);
302
+ // Keep only the most recent maxCount entries
303
+ if (history.length > maxCount)
304
+ history = history.slice(-maxCount);
305
+ db.run('UPDATE users SET passwordHistory = ? WHERE id = ?', [
306
+ JSON.stringify(history),
307
+ userId,
308
+ ]);
309
+ },
310
+ async findOrCreateByProvider(provider, providerId, profile) {
311
+ const key = `${provider}:${providerId}`;
312
+ // Find by provider key using json_each
313
+ const existing = db
314
+ .query('SELECT u.id FROM users u, json_each(u.providerIds) p WHERE p.value = ?')
315
+ .get(key);
316
+ if (existing)
317
+ return { id: existing.id, created: false };
318
+ // Reject if email belongs to a credential account
319
+ if (profile.email) {
320
+ const emailUser = db
321
+ .query('SELECT id FROM users WHERE email = ?')
322
+ .get(profile.email);
323
+ if (emailUser)
324
+ throw new HttpError(409, 'An account with this email already exists. Sign in with your credentials, then link Google from your account settings.');
325
+ }
326
+ const id = crypto.randomUUID();
327
+ const normalizedEmail = profile.email ? profile.email.toLowerCase() : null;
328
+ db.run('INSERT INTO users (id, email, identifier, providerIds) VALUES (?, ?, ?, ?)', [
329
+ id,
330
+ normalizedEmail,
331
+ normalizedEmail,
332
+ JSON.stringify([key]),
333
+ ]);
334
+ return { id, created: true };
335
+ },
336
+ async linkProvider(userId, provider, providerId) {
337
+ const key = `${provider}:${providerId}`;
338
+ const row = db
339
+ .query('SELECT id, providerIds FROM users WHERE id = ?')
340
+ .get(userId);
341
+ if (!row)
342
+ throw new HttpError(404, 'User not found');
343
+ const ids = JSON.parse(row.providerIds);
344
+ if (!ids.includes(key)) {
345
+ db.run('UPDATE users SET providerIds = ? WHERE id = ?', [
346
+ JSON.stringify([...ids, key]),
347
+ userId,
348
+ ]);
349
+ }
350
+ },
351
+ async getRoles(userId) {
352
+ const row = db
353
+ .query('SELECT roles FROM users WHERE id = ?')
354
+ .get(userId);
355
+ return row ? JSON.parse(row.roles) : [];
356
+ },
357
+ async setRoles(userId, roles) {
358
+ db.run('UPDATE users SET roles = ? WHERE id = ?', [JSON.stringify(roles), userId]);
359
+ },
360
+ async addRole(userId, role) {
361
+ const row = db
362
+ .query('SELECT roles FROM users WHERE id = ?')
363
+ .get(userId);
364
+ if (!row)
365
+ return;
366
+ const roles = JSON.parse(row.roles);
367
+ if (!roles.includes(role)) {
368
+ db.run('UPDATE users SET roles = ? WHERE id = ?', [
369
+ JSON.stringify([...roles, role]),
370
+ userId,
371
+ ]);
372
+ }
373
+ },
374
+ async removeRole(userId, role) {
375
+ const row = db
376
+ .query('SELECT roles FROM users WHERE id = ?')
377
+ .get(userId);
378
+ if (!row)
379
+ return;
380
+ const roles = JSON.parse(row.roles);
381
+ db.run('UPDATE users SET roles = ? WHERE id = ?', [
382
+ JSON.stringify(roles.filter(r => r !== role)),
383
+ userId,
384
+ ]);
385
+ },
386
+ async getUser(userId) {
387
+ const row = db
388
+ .query('SELECT email, providerIds, emailVerified, displayName, firstName, lastName, externalId, suspended, suspendedReason, user_metadata, app_metadata FROM users WHERE id = ?')
389
+ .get(userId);
390
+ if (!row)
391
+ return null;
392
+ const userMeta = JSON.parse(row.user_metadata || '{}');
393
+ const appMeta = JSON.parse(row.app_metadata || '{}');
394
+ return {
395
+ email: row.email ?? undefined,
396
+ providerIds: JSON.parse(row.providerIds),
397
+ emailVerified: row.emailVerified === 1,
398
+ displayName: row.displayName ?? undefined,
399
+ firstName: row.firstName ?? undefined,
400
+ lastName: row.lastName ?? undefined,
401
+ externalId: row.externalId ?? undefined,
402
+ suspended: row.suspended === 1,
403
+ suspendedReason: row.suspendedReason ?? undefined,
404
+ userMetadata: Object.keys(userMeta).length > 0 ? userMeta : undefined,
405
+ appMetadata: Object.keys(appMeta).length > 0 ? appMeta : undefined,
406
+ };
407
+ },
408
+ async unlinkProvider(userId, provider) {
409
+ const row = db
410
+ .query('SELECT providerIds FROM users WHERE id = ?')
411
+ .get(userId);
412
+ if (!row)
413
+ throw new HttpError(404, 'User not found');
414
+ const ids = JSON.parse(row.providerIds);
415
+ db.run('UPDATE users SET providerIds = ? WHERE id = ?', [
416
+ JSON.stringify(ids.filter(id => !id.startsWith(`${provider}:`))),
417
+ userId,
418
+ ]);
419
+ },
420
+ async findByIdentifier(value) {
421
+ const normalized = value.toLowerCase();
422
+ const row = db
423
+ .query('SELECT id, passwordHash FROM users WHERE identifier = ? OR email = ? LIMIT 1')
424
+ .get(normalized, normalized);
425
+ if (!row)
426
+ return null;
427
+ return { id: row.id, passwordHash: row.passwordHash ?? '' };
428
+ },
429
+ async setEmailVerified(userId, verified) {
430
+ db.run('UPDATE users SET emailVerified = ? WHERE id = ?', [verified ? 1 : 0, userId]);
431
+ },
432
+ async getEmailVerified(userId) {
433
+ const row = db
434
+ .query('SELECT emailVerified FROM users WHERE id = ?')
435
+ .get(userId);
436
+ return row?.emailVerified === 1;
437
+ },
438
+ async deleteUser(userId) {
439
+ db.transaction(() => {
440
+ db.run('DELETE FROM sessions WHERE userId = ?', [userId]);
441
+ db.run('DELETE FROM tenant_roles WHERE userId = ?', [userId]);
442
+ db.run('DELETE FROM group_memberships WHERE userId = ?', [userId]);
443
+ db.run('DELETE FROM webauthn_credentials WHERE userId = ?', [userId]);
444
+ db.run('DELETE FROM users WHERE id = ?', [userId]);
445
+ })();
446
+ },
447
+ async hasPassword(userId) {
448
+ const row = db
449
+ .query('SELECT passwordHash FROM users WHERE id = ?')
450
+ .get(userId);
451
+ return !!row?.passwordHash;
452
+ },
453
+ async setMfaSecret(userId, secret) {
454
+ db.run('UPDATE users SET mfaSecret = ? WHERE id = ?', [secret, userId]);
455
+ },
456
+ async getMfaSecret(userId) {
457
+ const row = db
458
+ .query('SELECT mfaSecret FROM users WHERE id = ?')
459
+ .get(userId);
460
+ return row?.mfaSecret ?? null;
461
+ },
462
+ async isMfaEnabled(userId) {
463
+ const row = db
464
+ .query('SELECT mfaEnabled FROM users WHERE id = ?')
465
+ .get(userId);
466
+ return row?.mfaEnabled === 1;
467
+ },
468
+ async setMfaEnabled(userId, enabled) {
469
+ db.run('UPDATE users SET mfaEnabled = ? WHERE id = ?', [enabled ? 1 : 0, userId]);
470
+ },
471
+ async setRecoveryCodes(userId, codes) {
472
+ db.run('UPDATE users SET recoveryCodes = ? WHERE id = ?', [JSON.stringify(codes), userId]);
473
+ },
474
+ async getRecoveryCodes(userId) {
475
+ const row = db
476
+ .query('SELECT recoveryCodes FROM users WHERE id = ?')
477
+ .get(userId);
478
+ return row?.recoveryCodes ? JSON.parse(row.recoveryCodes) : [];
479
+ },
480
+ async removeRecoveryCode(userId, code) {
481
+ db.transaction(() => {
482
+ const row = db
483
+ .query('SELECT recoveryCodes FROM users WHERE id = ?')
484
+ .get(userId);
485
+ if (!row)
486
+ return;
487
+ let codes = [];
488
+ try {
489
+ codes = JSON.parse(row.recoveryCodes || '[]');
490
+ }
491
+ catch {
492
+ return;
493
+ }
494
+ const idx = codes.indexOf(code);
495
+ if (idx !== -1) {
496
+ codes.splice(idx, 1);
497
+ db.run('UPDATE users SET recoveryCodes = ? WHERE id = ?', [
498
+ JSON.stringify(codes),
499
+ userId,
500
+ ]);
501
+ }
502
+ })();
503
+ },
504
+ consumeRecoveryCode(userId, hashedCode) {
505
+ // Wrap the read-splice-write in a SQLite transaction so it is atomic.
506
+ const result = db.transaction(() => {
507
+ const row = db
508
+ .query('SELECT recoveryCodes FROM users WHERE id = ?')
509
+ .get(userId);
510
+ if (!row)
511
+ return false;
512
+ let codes = [];
513
+ try {
514
+ codes = JSON.parse(row.recoveryCodes || '[]');
515
+ }
516
+ catch {
517
+ return false;
518
+ }
519
+ const idx = codes.indexOf(hashedCode);
520
+ if (idx === -1)
521
+ return false;
522
+ codes.splice(idx, 1);
523
+ db.run('UPDATE users SET recoveryCodes = ? WHERE id = ?', [JSON.stringify(codes), userId]);
524
+ return true;
525
+ })();
526
+ return Promise.resolve(result);
527
+ },
528
+ async getMfaMethods(userId) {
529
+ const row = db
530
+ .query('SELECT mfaMethods FROM users WHERE id = ?')
531
+ .get(userId);
532
+ return row?.mfaMethods ? JSON.parse(row.mfaMethods) : [];
533
+ },
534
+ async setMfaMethods(userId, methods) {
535
+ db.run('UPDATE users SET mfaMethods = ? WHERE id = ?', [JSON.stringify(methods), userId]);
536
+ },
537
+ async getWebAuthnCredentials(userId) {
538
+ const rows = db
539
+ .query('SELECT credentialId, publicKey, signCount, transports, name, createdAt FROM webauthn_credentials WHERE userId = ?')
540
+ .all(userId);
541
+ return rows.map(r => ({
542
+ credentialId: r.credentialId,
543
+ publicKey: r.publicKey,
544
+ signCount: r.signCount,
545
+ transports: JSON.parse(r.transports),
546
+ name: r.name ?? undefined,
547
+ createdAt: r.createdAt,
548
+ }));
549
+ },
550
+ async addWebAuthnCredential(userId, credential) {
551
+ db.run('INSERT INTO webauthn_credentials (credentialId, userId, publicKey, signCount, transports, name, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?)', [
552
+ credential.credentialId,
553
+ userId,
554
+ credential.publicKey,
555
+ credential.signCount,
556
+ JSON.stringify(credential.transports ?? []),
557
+ credential.name ?? null,
558
+ credential.createdAt,
559
+ ]);
560
+ },
561
+ async removeWebAuthnCredential(userId, credentialId) {
562
+ db.run('DELETE FROM webauthn_credentials WHERE credentialId = ? AND userId = ?', [
563
+ credentialId,
564
+ userId,
565
+ ]);
566
+ },
567
+ async updateWebAuthnCredentialSignCount(userId, credentialId, signCount) {
568
+ db.run('UPDATE webauthn_credentials SET signCount = ? WHERE credentialId = ? AND userId = ?', [signCount, credentialId, userId]);
569
+ },
570
+ async findUserByWebAuthnCredentialId(credentialId) {
571
+ const row = db
572
+ .query('SELECT userId FROM webauthn_credentials WHERE credentialId = ?')
573
+ .get(credentialId);
574
+ return row?.userId ?? null;
575
+ },
576
+ async getTenantRoles(userId, tenantId) {
577
+ const rows = db
578
+ .query('SELECT role FROM tenant_roles WHERE userId = ? AND tenantId = ?')
579
+ .all(userId, tenantId);
580
+ return rows.map(r => r.role);
581
+ },
582
+ async setTenantRoles(userId, tenantId, roles) {
583
+ db.run('DELETE FROM tenant_roles WHERE userId = ? AND tenantId = ?', [userId, tenantId]);
584
+ const stmt = db.prepare('INSERT INTO tenant_roles (userId, tenantId, role) VALUES (?, ?, ?)');
585
+ for (const role of roles) {
586
+ stmt.run(userId, tenantId, role);
587
+ }
588
+ },
589
+ async addTenantRole(userId, tenantId, role) {
590
+ try {
591
+ db.run('INSERT INTO tenant_roles (userId, tenantId, role) VALUES (?, ?, ?)', [
592
+ userId,
593
+ tenantId,
594
+ role,
595
+ ]);
596
+ }
597
+ catch {
598
+ /* already exists */
599
+ }
600
+ },
601
+ async removeTenantRole(userId, tenantId, role) {
602
+ db.run('DELETE FROM tenant_roles WHERE userId = ? AND tenantId = ? AND role = ?', [
603
+ userId,
604
+ tenantId,
605
+ role,
606
+ ]);
607
+ },
608
+ async setSuspended(userId, suspended, reason) {
609
+ if (suspended) {
610
+ db.run('UPDATE users SET suspended = 1, suspendedAt = ?, suspendedReason = ? WHERE id = ?', [new Date().toISOString(), reason ?? null, userId]);
611
+ }
612
+ else {
613
+ db.run('UPDATE users SET suspended = 0, suspendedAt = NULL, suspendedReason = NULL WHERE id = ?', [userId]);
614
+ }
615
+ },
616
+ async getSuspended(userId) {
617
+ const row = db
618
+ .query('SELECT suspended, suspendedReason FROM users WHERE id = ?')
619
+ .get(userId);
620
+ if (!row)
621
+ return null;
622
+ return { suspended: row.suspended === 1, suspendedReason: row.suspendedReason ?? undefined };
623
+ },
624
+ async updateProfile(userId, fields) {
625
+ const sets = [];
626
+ const params = [];
627
+ if ('displayName' in fields) {
628
+ sets.push('displayName = ?');
629
+ params.push(fields.displayName ?? null);
630
+ }
631
+ if ('firstName' in fields) {
632
+ sets.push('firstName = ?');
633
+ params.push(fields.firstName ?? null);
634
+ }
635
+ if ('lastName' in fields) {
636
+ sets.push('lastName = ?');
637
+ params.push(fields.lastName ?? null);
638
+ }
639
+ if ('externalId' in fields) {
640
+ sets.push('externalId = ?');
641
+ params.push(fields.externalId ?? null);
642
+ }
643
+ if ('userMetadata' in fields) {
644
+ sets.push('user_metadata = ?');
645
+ params.push(JSON.stringify(fields.userMetadata ?? {}));
646
+ }
647
+ if (sets.length === 0)
648
+ return;
649
+ params.push(userId);
650
+ db.run(`UPDATE users SET ${sets.join(', ')} WHERE id = ?`, params);
651
+ },
652
+ async getUserMetadata(userId) {
653
+ const row = db
654
+ .query('SELECT user_metadata, app_metadata FROM users WHERE id = ?')
655
+ .get(userId);
656
+ if (!row)
657
+ return {};
658
+ const userMeta = JSON.parse(row.user_metadata || '{}');
659
+ const appMeta = JSON.parse(row.app_metadata || '{}');
660
+ return {
661
+ userMetadata: Object.keys(userMeta).length > 0 ? userMeta : undefined,
662
+ appMetadata: Object.keys(appMeta).length > 0 ? appMeta : undefined,
663
+ };
664
+ },
665
+ async setUserMetadata(userId, data) {
666
+ db.run('UPDATE users SET user_metadata = ? WHERE id = ?', [JSON.stringify(data), userId]);
667
+ },
668
+ async setAppMetadata(userId, data) {
669
+ db.run('UPDATE users SET app_metadata = ? WHERE id = ?', [JSON.stringify(data), userId]);
670
+ },
671
+ async listUsers(query) {
672
+ const conditions = [];
673
+ const params = [];
674
+ if (query.email !== undefined) {
675
+ conditions.push('email = ?');
676
+ params.push(query.email);
677
+ }
678
+ if (query.externalId !== undefined) {
679
+ conditions.push('externalId = ?');
680
+ params.push(query.externalId);
681
+ }
682
+ if (query.suspended !== undefined) {
683
+ conditions.push('suspended = ?');
684
+ params.push(query.suspended ? 1 : 0);
685
+ }
686
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
687
+ const startIndex = query.startIndex ?? 0;
688
+ const count = query.count ?? 100;
689
+ const queryParams = [...params, count, startIndex];
690
+ const countParams = params;
691
+ const rows = db
692
+ .prepare(`SELECT id, email, displayName, firstName, lastName, externalId, suspended, suspendedAt, suspendedReason, emailVerified, providerIds FROM users ${where} LIMIT ? OFFSET ?`)
693
+ .all(...queryParams);
694
+ const totalRow = db
695
+ .prepare(`SELECT COUNT(*) as c FROM users ${where}`)
696
+ .get(...countParams);
697
+ const totalResults = totalRow?.c ?? 0;
698
+ return {
699
+ users: rows.map(r => ({
700
+ id: r.id,
701
+ email: r.email ?? undefined,
702
+ displayName: r.displayName ?? undefined,
703
+ firstName: r.firstName ?? undefined,
704
+ lastName: r.lastName ?? undefined,
705
+ externalId: r.externalId ?? undefined,
706
+ suspended: r.suspended === 1,
707
+ suspendedAt: r.suspendedAt ? new Date(r.suspendedAt) : undefined,
708
+ suspendedReason: r.suspendedReason ?? undefined,
709
+ emailVerified: r.emailVerified === 1,
710
+ providerIds: JSON.parse(r.providerIds),
711
+ })),
712
+ totalResults,
713
+ };
714
+ },
715
+ // ---------------------------------------------------------------------------
716
+ // Groups
717
+ // ---------------------------------------------------------------------------
718
+ async createGroup(group) {
719
+ const id = crypto.randomUUID();
720
+ const now = Date.now();
721
+ try {
722
+ db.run('INSERT INTO groups (id, name, displayName, description, roles, tenantId, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
723
+ id,
724
+ group.name,
725
+ group.displayName ?? null,
726
+ group.description ?? null,
727
+ JSON.stringify(group.roles ?? []),
728
+ group.tenantId ?? null,
729
+ now,
730
+ now,
731
+ ]);
732
+ }
733
+ catch (err) {
734
+ if (err?.code === 'SQLITE_CONSTRAINT_UNIQUE' ||
735
+ err?.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') {
736
+ throw new HttpError(409, 'A group with this name already exists in this scope');
737
+ }
738
+ throw err;
739
+ }
740
+ return { id };
741
+ },
742
+ async deleteGroup(groupId) {
743
+ // group_memberships are cascade-deleted via ON DELETE CASCADE (requires PRAGMA foreign_keys = ON)
744
+ db.run('DELETE FROM groups WHERE id = ?', [groupId]);
745
+ },
746
+ async getGroup(groupId) {
747
+ const row = db
748
+ .query('SELECT id, name, displayName, description, roles, tenantId, createdAt, updatedAt FROM groups WHERE id = ?')
749
+ .get(groupId);
750
+ if (!row)
751
+ return null;
752
+ return {
753
+ ...row,
754
+ displayName: row.displayName ?? undefined,
755
+ description: row.description ?? undefined,
756
+ roles: JSON.parse(row.roles),
757
+ };
758
+ },
759
+ async listGroups(tenantId, opts) {
760
+ const limit = Math.min(opts?.limit ?? 50, 200);
761
+ const cols = 'id, name, displayName, description, roles, tenantId, createdAt, updatedAt';
762
+ const conditions = [];
763
+ const params = [];
764
+ if (tenantId === null) {
765
+ conditions.push('tenantId IS NULL');
766
+ }
767
+ else {
768
+ conditions.push('tenantId = ?');
769
+ params.push(tenantId);
770
+ }
771
+ if (opts?.cursor) {
772
+ const c = decodeSqliteCursor(opts.cursor);
773
+ if (c) {
774
+ conditions.push('(createdAt > ? OR (createdAt = ? AND id > ?))');
775
+ params.push(c.createdAt, c.createdAt, c.id);
776
+ }
777
+ }
778
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
779
+ params.push(limit + 1);
780
+ const rows = db
781
+ .query(`SELECT ${cols} FROM groups ${where} ORDER BY createdAt ASC, id ASC LIMIT ?`)
782
+ .all(...params);
783
+ const hasMore = rows.length > limit;
784
+ const page = hasMore ? rows.slice(0, limit) : rows;
785
+ const nextCursor = hasMore
786
+ ? encodeSqliteCursor(page[page.length - 1].createdAt, page[page.length - 1].id)
787
+ : undefined;
788
+ const items = page.map(r => ({
789
+ ...r,
790
+ displayName: r.displayName ?? undefined,
791
+ description: r.description ?? undefined,
792
+ roles: JSON.parse(r.roles),
793
+ }));
794
+ return { items, nextCursor };
795
+ },
796
+ async updateGroup(groupId, updates) {
797
+ const now = Date.now();
798
+ const sets = ['updatedAt = ?'];
799
+ const params = [now];
800
+ if (updates.name !== undefined) {
801
+ sets.push('name = ?');
802
+ params.push(updates.name);
803
+ }
804
+ if ('displayName' in updates) {
805
+ sets.push('displayName = ?');
806
+ params.push(updates.displayName ?? null);
807
+ }
808
+ if ('description' in updates) {
809
+ sets.push('description = ?');
810
+ params.push(updates.description ?? null);
811
+ }
812
+ if (updates.roles !== undefined) {
813
+ sets.push('roles = ?');
814
+ params.push(JSON.stringify(updates.roles));
815
+ }
816
+ params.push(groupId);
817
+ db.run(`UPDATE groups SET ${sets.join(', ')} WHERE id = ?`, params);
818
+ },
819
+ async addGroupMember(groupId, userId, roles = []) {
820
+ const group = db
821
+ .query('SELECT tenantId FROM groups WHERE id = ?')
822
+ .get(groupId);
823
+ if (!group)
824
+ throw new HttpError(404, 'Group not found');
825
+ try {
826
+ db.run('INSERT INTO group_memberships (userId, groupId, roles, tenantId, createdAt) VALUES (?, ?, ?, ?, ?)', [userId, groupId, JSON.stringify(roles), group.tenantId ?? null, Date.now()]);
827
+ }
828
+ catch (err) {
829
+ if (err?.code === 'SQLITE_CONSTRAINT_PRIMARYKEY' ||
830
+ err?.code === 'SQLITE_CONSTRAINT_UNIQUE') {
831
+ throw new HttpError(409, 'User is already a member of this group');
832
+ }
833
+ throw err;
834
+ }
835
+ },
836
+ async updateGroupMembership(groupId, userId, roles) {
837
+ db.run('UPDATE group_memberships SET roles = ? WHERE userId = ? AND groupId = ?', [
838
+ JSON.stringify(roles),
839
+ userId,
840
+ groupId,
841
+ ]);
842
+ },
843
+ async removeGroupMember(groupId, userId) {
844
+ db.run('DELETE FROM group_memberships WHERE userId = ? AND groupId = ?', [userId, groupId]);
845
+ },
846
+ async getGroupMembers(groupId, opts) {
847
+ const limit = Math.min(opts?.limit ?? 50, 200);
848
+ const conditions = ['groupId = ?'];
849
+ const params = [groupId];
850
+ if (opts?.cursor) {
851
+ const c = decodeSqliteCursor(opts.cursor);
852
+ if (c) {
853
+ conditions.push('(createdAt > ? OR (createdAt = ? AND userId > ?))');
854
+ params.push(c.createdAt, c.createdAt, c.id);
855
+ }
856
+ }
857
+ const where = conditions.join(' AND ');
858
+ params.push(limit + 1);
859
+ const rows = db
860
+ .query(`SELECT userId, roles, createdAt FROM group_memberships WHERE ${where} ORDER BY createdAt ASC, userId ASC LIMIT ?`)
861
+ .all(...params);
862
+ const hasMore = rows.length > limit;
863
+ const page = hasMore ? rows.slice(0, limit) : rows;
864
+ const nextCursor = hasMore
865
+ ? encodeSqliteCursor(page[page.length - 1].createdAt, page[page.length - 1].userId)
866
+ : undefined;
867
+ return {
868
+ items: page.map(r => ({ userId: r.userId, roles: JSON.parse(r.roles) })),
869
+ nextCursor,
870
+ };
871
+ },
872
+ async getUserGroups(userId, tenantId) {
873
+ let memberRows;
874
+ if (tenantId === null) {
875
+ memberRows = db
876
+ .query('SELECT groupId, roles as memberRoles FROM group_memberships WHERE userId = ? AND tenantId IS NULL')
877
+ .all(userId);
878
+ }
879
+ else {
880
+ memberRows = db
881
+ .query('SELECT groupId, roles as memberRoles FROM group_memberships WHERE userId = ? AND tenantId = ?')
882
+ .all(userId, tenantId);
883
+ }
884
+ if (memberRows.length === 0)
885
+ return [];
886
+ const result = [];
887
+ for (const m of memberRows) {
888
+ const row = db
889
+ .query('SELECT id, name, displayName, description, roles, tenantId, createdAt, updatedAt FROM groups WHERE id = ?')
890
+ .get(m.groupId);
891
+ if (row) {
892
+ result.push({
893
+ group: {
894
+ ...row,
895
+ displayName: row.displayName ?? undefined,
896
+ description: row.description ?? undefined,
897
+ roles: JSON.parse(row.roles),
898
+ },
899
+ membershipRoles: JSON.parse(m.memberRoles),
900
+ });
901
+ }
902
+ }
903
+ return result;
904
+ },
905
+ async getEffectiveRoles(userId, tenantId) {
906
+ // Direct roles
907
+ let direct = [];
908
+ if (tenantId) {
909
+ const rows = db
910
+ .query('SELECT role FROM tenant_roles WHERE userId = ? AND tenantId = ?')
911
+ .all(userId, tenantId);
912
+ direct = rows.map(r => r.role);
913
+ }
914
+ else {
915
+ const row = db
916
+ .query('SELECT roles FROM users WHERE id = ?')
917
+ .get(userId);
918
+ direct = row ? JSON.parse(row.roles) : [];
919
+ }
920
+ let memberRows;
921
+ if (tenantId === null) {
922
+ memberRows = db
923
+ .query('SELECT g.roles as groupRoles, gm.roles as memberRoles FROM group_memberships gm JOIN groups g ON g.id = gm.groupId WHERE gm.userId = ? AND gm.tenantId IS NULL')
924
+ .all(userId);
925
+ }
926
+ else {
927
+ memberRows = db
928
+ .query('SELECT g.roles as groupRoles, gm.roles as memberRoles FROM group_memberships gm JOIN groups g ON g.id = gm.groupId WHERE gm.userId = ? AND gm.tenantId = ?')
929
+ .all(userId, tenantId);
930
+ }
931
+ const groupRoles = memberRows.flatMap(r => [
932
+ ...JSON.parse(r.groupRoles),
933
+ ...JSON.parse(r.memberRoles),
934
+ ]);
935
+ return [...new Set([...direct, ...groupRoles])];
936
+ },
937
+ async getM2MClient(clientId) {
938
+ const row = db
939
+ .query('SELECT id, clientId, clientSecretHash, name, scopes, active FROM m2m_clients WHERE clientId = ? AND active = 1')
940
+ .get(clientId);
941
+ if (!row)
942
+ return null;
943
+ return {
944
+ id: row.id,
945
+ clientId: row.clientId,
946
+ clientSecretHash: row.clientSecretHash,
947
+ name: row.name,
948
+ scopes: JSON.parse(row.scopes),
949
+ active: row.active === 1,
950
+ };
951
+ },
952
+ async createM2MClient(data) {
953
+ const id = crypto.randomUUID();
954
+ db.run('INSERT INTO m2m_clients (id, clientId, clientSecretHash, name, scopes, active, createdAt) VALUES (?, ?, ?, ?, ?, 1, ?)', [
955
+ id,
956
+ data.clientId,
957
+ data.clientSecretHash,
958
+ data.name,
959
+ JSON.stringify(data.scopes),
960
+ Date.now(),
961
+ ]);
962
+ return { id };
963
+ },
964
+ async deleteM2MClient(clientId) {
965
+ db.run('DELETE FROM m2m_clients WHERE clientId = ?', [clientId]);
966
+ },
967
+ async listM2MClients() {
968
+ const rows = db
969
+ .query('SELECT id, clientId, name, scopes, active FROM m2m_clients')
970
+ .all();
971
+ return rows.map(row => ({
972
+ id: row.id,
973
+ clientId: row.clientId,
974
+ name: row.name,
975
+ scopes: JSON.parse(row.scopes),
976
+ active: row.active === 1,
977
+ }));
978
+ },
979
+ };
980
+ // ---------------------------------------------------------------------------
981
+ // Session helpers
982
+ // ---------------------------------------------------------------------------
983
+ function deleteSessionImpl(sessionId, getConfig) {
984
+ if (getConfig().persistSessionMetadata) {
985
+ db.run('UPDATE sessions SET token = NULL, refreshToken = NULL, prevRefreshToken = NULL, prevTokenExpiresAt = NULL WHERE sessionId = ?', [sessionId]);
986
+ }
987
+ else {
988
+ db.run('DELETE FROM sessions WHERE sessionId = ?', [sessionId]);
989
+ }
990
+ }
991
+ const OAUTH_STATE_TTL_MS = 5 * 60 * 1000; // 5 minutes
992
+ const result = {
993
+ db,
994
+ adapter,
995
+ cleanupInterval: null,
996
+ atomicCreateSession(userId, token, sessionId, maxSessions, getConfig, metadata) {
997
+ const now = Date.now();
998
+ const ttlMs = getSessionTtlMs(getConfig);
999
+ const expiresAt = now + ttlMs;
1000
+ db.transaction(() => {
1001
+ const countRow = db
1002
+ .query('SELECT COUNT(*) AS count FROM sessions WHERE userId = ? AND token IS NOT NULL AND expiresAt > ?')
1003
+ .get(userId, now);
1004
+ let activeCount = countRow?.count ?? 0;
1005
+ while (activeCount >= maxSessions) {
1006
+ const oldest = db
1007
+ .query('SELECT sessionId FROM sessions WHERE userId = ? AND token IS NOT NULL AND expiresAt > ? ORDER BY createdAt ASC LIMIT 1')
1008
+ .get(userId, now);
1009
+ if (!oldest)
1010
+ break;
1011
+ if (getConfig().persistSessionMetadata) {
1012
+ db.run('UPDATE sessions SET token = NULL, refreshToken = NULL, prevRefreshToken = NULL, prevTokenExpiresAt = NULL WHERE sessionId = ?', [oldest.sessionId]);
1013
+ }
1014
+ else {
1015
+ db.run('DELETE FROM sessions WHERE sessionId = ?', [oldest.sessionId]);
1016
+ }
1017
+ activeCount--;
1018
+ }
1019
+ db.run('INSERT INTO sessions (sessionId, userId, token, createdAt, lastActiveAt, expiresAt, ipAddress, userAgent) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
1020
+ sessionId,
1021
+ userId,
1022
+ token,
1023
+ now,
1024
+ now,
1025
+ expiresAt,
1026
+ metadata?.ipAddress ?? null,
1027
+ metadata?.userAgent ?? null,
1028
+ ]);
1029
+ })();
1030
+ },
1031
+ createSession(userId, token, sessionId, getConfig, metadata) {
1032
+ const now = Date.now();
1033
+ const expiresAt = now + getSessionTtlMs(getConfig);
1034
+ db.run('INSERT INTO sessions (sessionId, userId, token, createdAt, lastActiveAt, expiresAt, ipAddress, userAgent) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
1035
+ sessionId,
1036
+ userId,
1037
+ token,
1038
+ now,
1039
+ now,
1040
+ expiresAt,
1041
+ metadata?.ipAddress ?? null,
1042
+ metadata?.userAgent ?? null,
1043
+ ]);
1044
+ },
1045
+ getSession(sessionId) {
1046
+ const row = db
1047
+ .query('SELECT token FROM sessions WHERE sessionId = ? AND expiresAt > ?')
1048
+ .get(sessionId, Date.now());
1049
+ if (!row || !row.token)
1050
+ return null;
1051
+ return row.token;
1052
+ },
1053
+ getSessionRecord(sessionId) {
1054
+ const row = db
1055
+ .query('SELECT token, lastActiveAt FROM sessions WHERE sessionId = ? AND expiresAt > ?')
1056
+ .get(sessionId, Date.now());
1057
+ if (!row || !row.token)
1058
+ return null;
1059
+ return { token: row.token, lastActiveAt: row.lastActiveAt };
1060
+ },
1061
+ deleteSession: deleteSessionImpl,
1062
+ getUserSessions(userId, getConfig) {
1063
+ const now = Date.now();
1064
+ const rows = db
1065
+ .query('SELECT sessionId, token, createdAt, lastActiveAt, expiresAt, ipAddress, userAgent FROM sessions WHERE userId = ? ORDER BY createdAt ASC')
1066
+ .all(userId);
1067
+ const cfg = getConfig();
1068
+ const includeInactive = cfg.includeInactiveSessions;
1069
+ const persist = cfg.persistSessionMetadata;
1070
+ const results = [];
1071
+ for (const row of rows) {
1072
+ const isActive = !!row.token && row.expiresAt > now;
1073
+ if (!isActive && !persist)
1074
+ continue;
1075
+ if (!isActive && !includeInactive)
1076
+ continue;
1077
+ results.push({
1078
+ sessionId: row.sessionId,
1079
+ createdAt: row.createdAt,
1080
+ lastActiveAt: row.lastActiveAt,
1081
+ expiresAt: row.expiresAt,
1082
+ ipAddress: row.ipAddress ?? undefined,
1083
+ userAgent: row.userAgent ?? undefined,
1084
+ isActive,
1085
+ });
1086
+ }
1087
+ return results;
1088
+ },
1089
+ getActiveSessionCount(userId) {
1090
+ const row = db
1091
+ .query('SELECT COUNT(*) AS count FROM sessions WHERE userId = ? AND token IS NOT NULL AND expiresAt > ?')
1092
+ .get(userId, Date.now());
1093
+ return row?.count ?? 0;
1094
+ },
1095
+ evictOldestSession(userId, getConfig) {
1096
+ const now = Date.now();
1097
+ const oldest = db
1098
+ .query('SELECT sessionId FROM sessions WHERE userId = ? AND token IS NOT NULL AND expiresAt > ? ORDER BY createdAt ASC LIMIT 1')
1099
+ .get(userId, now);
1100
+ if (oldest)
1101
+ deleteSessionImpl(oldest.sessionId, getConfig);
1102
+ },
1103
+ updateSessionLastActive(sessionId) {
1104
+ db.run('UPDATE sessions SET lastActiveAt = ? WHERE sessionId = ?', [Date.now(), sessionId]);
1105
+ },
1106
+ setRefreshToken(sessionId, refreshToken) {
1107
+ const tokenHash = hashToken(refreshToken);
1108
+ db.run('UPDATE sessions SET refreshToken = ? WHERE sessionId = ?', [tokenHash, sessionId]);
1109
+ },
1110
+ getSessionByRefreshToken(refreshToken, getConfig) {
1111
+ const tokenHash = hashToken(refreshToken);
1112
+ // Check current refresh token (stored as hash)
1113
+ let row = db
1114
+ .query('SELECT sessionId, userId, refreshToken FROM sessions WHERE refreshToken = ?')
1115
+ .get(tokenHash);
1116
+ if (row) {
1117
+ return { sessionId: row.sessionId, userId: row.userId, newRefreshToken: refreshToken };
1118
+ }
1119
+ const graceRow = db
1120
+ .query('SELECT sessionId, userId, refreshToken, refreshTokenPlain, prevTokenExpiresAt FROM sessions WHERE prevRefreshToken = ?')
1121
+ .get(tokenHash);
1122
+ if (!graceRow)
1123
+ return null;
1124
+ if (graceRow.prevTokenExpiresAt && graceRow.prevTokenExpiresAt > Date.now()) {
1125
+ // Within grace window — return current plain refresh token so caller can re-issue it directly
1126
+ return {
1127
+ sessionId: graceRow.sessionId,
1128
+ userId: graceRow.userId,
1129
+ newRefreshToken: graceRow.refreshTokenPlain ?? graceRow.refreshToken,
1130
+ };
1131
+ }
1132
+ // Grace window expired — theft detected, invalidate session
1133
+ deleteSessionImpl(graceRow.sessionId, getConfig);
1134
+ return null;
1135
+ },
1136
+ rotateRefreshToken(sessionId, newRefreshToken, newAccessToken, getConfig) {
1137
+ const graceSeconds = getConfig().refreshToken?.rotationGraceSeconds ?? 30;
1138
+ const prevTokenExpiresAt = Date.now() + graceSeconds * 1000;
1139
+ const newHash = hashToken(newRefreshToken);
1140
+ db.run('UPDATE sessions SET prevRefreshToken = refreshToken, prevTokenExpiresAt = ?, refreshToken = ?, refreshTokenPlain = ?, token = ? WHERE sessionId = ?', [prevTokenExpiresAt, newHash, newRefreshToken, newAccessToken, sessionId]);
1141
+ },
1142
+ getSessionFingerprint(sessionId) {
1143
+ const row = db
1144
+ .query('SELECT fingerprint FROM sessions WHERE sessionId = ?')
1145
+ .get(sessionId);
1146
+ return row?.fingerprint ?? null;
1147
+ },
1148
+ setSessionFingerprint(sessionId, fingerprint) {
1149
+ db.run('UPDATE sessions SET fingerprint = ? WHERE sessionId = ?', [fingerprint, sessionId]);
1150
+ },
1151
+ getMfaVerifiedAt(sessionId) {
1152
+ const row = db
1153
+ .query('SELECT mfaVerifiedAt FROM sessions WHERE sessionId = ?')
1154
+ .get(sessionId);
1155
+ return row?.mfaVerifiedAt ?? null;
1156
+ },
1157
+ setMfaVerifiedAt(sessionId, ts) {
1158
+ db.run('UPDATE sessions SET mfaVerifiedAt = ? WHERE sessionId = ?', [ts, sessionId]);
1159
+ },
1160
+ storeOAuthState(state, codeVerifier, linkUserId) {
1161
+ const expiresAt = Date.now() + OAUTH_STATE_TTL_MS;
1162
+ db.run('INSERT INTO oauth_states (state, codeVerifier, linkUserId, expiresAt) VALUES (?, ?, ?, ?)', [state, codeVerifier ?? null, linkUserId ?? null, expiresAt]);
1163
+ },
1164
+ consumeOAuthState(state) {
1165
+ const row = db
1166
+ .query('DELETE FROM oauth_states WHERE state = ? AND expiresAt > ? RETURNING codeVerifier, linkUserId')
1167
+ .get(state, Date.now());
1168
+ if (!row)
1169
+ return null;
1170
+ return {
1171
+ codeVerifier: row.codeVerifier ?? undefined,
1172
+ linkUserId: row.linkUserId ?? undefined,
1173
+ };
1174
+ },
1175
+ getCache(key) {
1176
+ const row = db
1177
+ .query('SELECT value FROM cache_entries WHERE key = ? AND (expiresAt IS NULL OR expiresAt > ?)')
1178
+ .get(key, Date.now());
1179
+ return row?.value ?? null;
1180
+ },
1181
+ setCache(key, value, ttlSeconds) {
1182
+ const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1000 : null;
1183
+ db.run('INSERT INTO cache_entries (key, value, expiresAt) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value, expiresAt = excluded.expiresAt', [key, value, expiresAt]);
1184
+ },
1185
+ delCache(key) {
1186
+ db.run('DELETE FROM cache_entries WHERE key = ?', [key]);
1187
+ },
1188
+ delCachePattern(pattern) {
1189
+ // Convert glob pattern (* wildcard) to a SQL LIKE pattern (% wildcard)
1190
+ const likePattern = pattern.replace(/%/g, '\\%').replace(/_/g, '\\_').replace(/\*/g, '%');
1191
+ db.run("DELETE FROM cache_entries WHERE key LIKE ? ESCAPE '\\'", [likePattern]);
1192
+ },
1193
+ createVerificationToken(token, userId, email, ttlSeconds) {
1194
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1195
+ db.run('INSERT INTO email_verifications (token, userId, email, expiresAt) VALUES (?, ?, ?, ?)', [token, userId, email, expiresAt]);
1196
+ },
1197
+ getVerificationToken(token) {
1198
+ const row = db
1199
+ .query('SELECT userId, email FROM email_verifications WHERE token = ? AND expiresAt > ?')
1200
+ .get(token, Date.now());
1201
+ return row ?? null;
1202
+ },
1203
+ deleteVerificationToken(token) {
1204
+ db.run('DELETE FROM email_verifications WHERE token = ?', [token]);
1205
+ },
1206
+ consumeVerificationToken(token) {
1207
+ const row = db
1208
+ .query('DELETE FROM email_verifications WHERE token = ? AND expiresAt > ? RETURNING userId, email')
1209
+ .get(token, Date.now());
1210
+ return row ?? null;
1211
+ },
1212
+ createResetToken(token, userId, email, ttlSeconds) {
1213
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1214
+ db.run('INSERT INTO password_resets (token, userId, email, expiresAt) VALUES (?, ?, ?, ?)', [
1215
+ token,
1216
+ userId,
1217
+ email,
1218
+ expiresAt,
1219
+ ]);
1220
+ },
1221
+ consumeResetToken(hash) {
1222
+ const row = db
1223
+ .query('DELETE FROM password_resets WHERE token = ? AND expiresAt > ? RETURNING userId, email')
1224
+ .get(hash, Date.now());
1225
+ return row ?? null;
1226
+ },
1227
+ createDeletionCancelToken(token, userId, jobId, ttlSeconds) {
1228
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1229
+ db.run('INSERT INTO deletion_cancel_tokens (token, userId, jobId, expiresAt) VALUES (?, ?, ?, ?)', [token, userId, jobId, expiresAt]);
1230
+ },
1231
+ consumeDeletionCancelToken(hash) {
1232
+ const row = db
1233
+ .query('DELETE FROM deletion_cancel_tokens WHERE token = ? AND expiresAt > ? RETURNING userId, jobId')
1234
+ .get(hash, Date.now());
1235
+ return row ?? null;
1236
+ },
1237
+ storeOAuthCode(hash, payload, ttlSeconds) {
1238
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1239
+ db.run('INSERT INTO oauth_codes (codeHash, token, userId, email, refreshToken, expiresAt) VALUES (?, ?, ?, ?, ?, ?)', [
1240
+ hash,
1241
+ payload.token,
1242
+ payload.userId,
1243
+ payload.email ?? null,
1244
+ payload.refreshToken ?? null,
1245
+ expiresAt,
1246
+ ]);
1247
+ },
1248
+ consumeOAuthCode(hash) {
1249
+ const row = db
1250
+ .query('DELETE FROM oauth_codes WHERE codeHash = ? AND expiresAt > ? RETURNING token, userId, email, refreshToken')
1251
+ .get(hash, Date.now());
1252
+ if (!row)
1253
+ return null;
1254
+ return {
1255
+ token: row.token,
1256
+ userId: row.userId,
1257
+ email: row.email ?? undefined,
1258
+ refreshToken: row.refreshToken ?? undefined,
1259
+ };
1260
+ },
1261
+ storeOAuthReauth(hash, data, ttlSeconds) {
1262
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1263
+ db.run('INSERT INTO oauth_reauth_states (tokenHash, userId, sessionId, provider, purpose, returnUrl, expiresAt) VALUES (?, ?, ?, ?, ?, ?, ?)', [
1264
+ hash,
1265
+ data.userId,
1266
+ data.sessionId,
1267
+ data.provider,
1268
+ data.purpose,
1269
+ data.returnUrl ?? null,
1270
+ expiresAt,
1271
+ ]);
1272
+ },
1273
+ consumeOAuthReauth(hash) {
1274
+ const row = db
1275
+ .query('DELETE FROM oauth_reauth_states WHERE tokenHash = ? AND expiresAt > ? RETURNING userId, sessionId, provider, purpose, returnUrl, expiresAt')
1276
+ .get(hash, Date.now());
1277
+ if (!row)
1278
+ return null;
1279
+ return {
1280
+ userId: row.userId,
1281
+ sessionId: row.sessionId,
1282
+ provider: row.provider,
1283
+ purpose: row.purpose,
1284
+ expiresAt: row.expiresAt,
1285
+ returnUrl: row.returnUrl ?? undefined,
1286
+ };
1287
+ },
1288
+ storeOAuthReauthConfirmation(hash, data, ttlSeconds) {
1289
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1290
+ db.run('INSERT INTO oauth_reauth_confirmations (codeHash, userId, purpose, expiresAt) VALUES (?, ?, ?, ?)', [hash, data.userId, data.purpose, expiresAt]);
1291
+ },
1292
+ consumeOAuthReauthConfirmation(hash) {
1293
+ const row = db
1294
+ .query('DELETE FROM oauth_reauth_confirmations WHERE codeHash = ? AND expiresAt > ? RETURNING userId, purpose')
1295
+ .get(hash, Date.now());
1296
+ if (!row)
1297
+ return null;
1298
+ return { userId: row.userId, purpose: row.purpose };
1299
+ },
1300
+ registerUpload(record) {
1301
+ db.run(`INSERT OR REPLACE INTO upload_registry (key, ownerUserId, tenantId, mimeType, bucket, createdAt)
1302
+ VALUES (?, ?, ?, ?, ?, ?)`, [
1303
+ record.key,
1304
+ record.ownerUserId ?? null,
1305
+ record.tenantId ?? null,
1306
+ record.mimeType ?? null,
1307
+ record.bucket ?? null,
1308
+ record.createdAt,
1309
+ ]);
1310
+ },
1311
+ getUploadRecord(key) {
1312
+ const row = db
1313
+ .query('SELECT key, ownerUserId, tenantId, mimeType, bucket, createdAt FROM upload_registry WHERE key = ?')
1314
+ .get(key);
1315
+ if (!row)
1316
+ return null;
1317
+ return {
1318
+ key: row.key,
1319
+ ...(row.ownerUserId !== null ? { ownerUserId: row.ownerUserId } : {}),
1320
+ ...(row.tenantId !== null ? { tenantId: row.tenantId } : {}),
1321
+ ...(row.mimeType !== null ? { mimeType: row.mimeType } : {}),
1322
+ ...(row.bucket !== null ? { bucket: row.bucket } : {}),
1323
+ createdAt: row.createdAt,
1324
+ };
1325
+ },
1326
+ deleteUploadRecord(key) {
1327
+ const r = db.run('DELETE FROM upload_registry WHERE key = ?', [key]);
1328
+ return r.changes > 0;
1329
+ },
1330
+ createMagicLinkToken(token, userId, ttlSeconds) {
1331
+ const expiresAt = Date.now() + ttlSeconds * 1000;
1332
+ db.run('INSERT INTO magic_link_tokens (token, userId, expiresAt) VALUES (?, ?, ?)', [
1333
+ token,
1334
+ userId,
1335
+ expiresAt,
1336
+ ]);
1337
+ },
1338
+ consumeMagicLinkToken(hash) {
1339
+ const row = db
1340
+ .query('DELETE FROM magic_link_tokens WHERE token = ? AND expiresAt > ? RETURNING userId')
1341
+ .get(hash, Date.now());
1342
+ return row?.userId ?? null;
1343
+ },
1344
+ startCleanup(getConfig, intervalMs = 3_600_000) {
1345
+ // Clear any existing interval to prevent duplicates
1346
+ if (_cleanupInterval !== null) {
1347
+ clearInterval(_cleanupInterval);
1348
+ }
1349
+ runCleanup(getConfig);
1350
+ const handle = setInterval(() => {
1351
+ runCleanup(getConfig);
1352
+ }, intervalMs);
1353
+ _cleanupInterval = handle;
1354
+ result.cleanupInterval = handle;
1355
+ return handle;
1356
+ },
1357
+ stopCleanup() {
1358
+ if (_cleanupInterval !== null) {
1359
+ clearInterval(_cleanupInterval);
1360
+ _cleanupInterval = null;
1361
+ result.cleanupInterval = null;
1362
+ }
1363
+ },
1364
+ };
1365
+ return result;
1366
+ }