@portaidentity/server 1.7.0

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/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/applications/cache.d.ts +64 -0
  5. package/dist/applications/cache.d.ts.map +1 -0
  6. package/dist/applications/cache.js +161 -0
  7. package/dist/applications/cache.js.map +1 -0
  8. package/dist/applications/errors.d.ts +25 -0
  9. package/dist/applications/errors.d.ts.map +1 -0
  10. package/dist/applications/errors.js +31 -0
  11. package/dist/applications/errors.js.map +1 -0
  12. package/dist/applications/index.d.ts +17 -0
  13. package/dist/applications/index.d.ts.map +1 -0
  14. package/dist/applications/index.js +19 -0
  15. package/dist/applications/index.js.map +1 -0
  16. package/dist/applications/repository.d.ts +184 -0
  17. package/dist/applications/repository.d.ts.map +1 -0
  18. package/dist/applications/repository.js +367 -0
  19. package/dist/applications/repository.js.map +1 -0
  20. package/dist/applications/service.d.ts +160 -0
  21. package/dist/applications/service.d.ts.map +1 -0
  22. package/dist/applications/service.js +388 -0
  23. package/dist/applications/service.js.map +1 -0
  24. package/dist/applications/slugs.d.ts +61 -0
  25. package/dist/applications/slugs.d.ts.map +1 -0
  26. package/dist/applications/slugs.js +115 -0
  27. package/dist/applications/slugs.js.map +1 -0
  28. package/dist/applications/types.d.ts +129 -0
  29. package/dist/applications/types.d.ts.map +1 -0
  30. package/dist/applications/types.js +62 -0
  31. package/dist/applications/types.js.map +1 -0
  32. package/dist/auth/csrf.d.ts +71 -0
  33. package/dist/auth/csrf.d.ts.map +1 -0
  34. package/dist/auth/csrf.js +112 -0
  35. package/dist/auth/csrf.js.map +1 -0
  36. package/dist/auth/email-renderer.d.ts +37 -0
  37. package/dist/auth/email-renderer.d.ts.map +1 -0
  38. package/dist/auth/email-renderer.js +116 -0
  39. package/dist/auth/email-renderer.js.map +1 -0
  40. package/dist/auth/email-service.d.ts +154 -0
  41. package/dist/auth/email-service.d.ts.map +1 -0
  42. package/dist/auth/email-service.js +415 -0
  43. package/dist/auth/email-service.js.map +1 -0
  44. package/dist/auth/email-transport.d.ts +65 -0
  45. package/dist/auth/email-transport.d.ts.map +1 -0
  46. package/dist/auth/email-transport.js +82 -0
  47. package/dist/auth/email-transport.js.map +1 -0
  48. package/dist/auth/i18n.d.ts +81 -0
  49. package/dist/auth/i18n.d.ts.map +1 -0
  50. package/dist/auth/i18n.js +395 -0
  51. package/dist/auth/i18n.js.map +1 -0
  52. package/dist/auth/index.d.ts +28 -0
  53. package/dist/auth/index.d.ts.map +1 -0
  54. package/dist/auth/index.js +31 -0
  55. package/dist/auth/index.js.map +1 -0
  56. package/dist/auth/interaction-authority.d.ts +39 -0
  57. package/dist/auth/interaction-authority.d.ts.map +1 -0
  58. package/dist/auth/interaction-authority.js +30 -0
  59. package/dist/auth/interaction-authority.js.map +1 -0
  60. package/dist/auth/magic-link-session.d.ts +81 -0
  61. package/dist/auth/magic-link-session.d.ts.map +1 -0
  62. package/dist/auth/magic-link-session.js +183 -0
  63. package/dist/auth/magic-link-session.js.map +1 -0
  64. package/dist/auth/rate-limiter.d.ts +143 -0
  65. package/dist/auth/rate-limiter.d.ts.map +1 -0
  66. package/dist/auth/rate-limiter.js +220 -0
  67. package/dist/auth/rate-limiter.js.map +1 -0
  68. package/dist/auth/recovery-crypto.d.ts +34 -0
  69. package/dist/auth/recovery-crypto.d.ts.map +1 -0
  70. package/dist/auth/recovery-crypto.js +103 -0
  71. package/dist/auth/recovery-crypto.js.map +1 -0
  72. package/dist/auth/recovery-job-processor.d.ts +24 -0
  73. package/dist/auth/recovery-job-processor.d.ts.map +1 -0
  74. package/dist/auth/recovery-job-processor.js +123 -0
  75. package/dist/auth/recovery-job-processor.js.map +1 -0
  76. package/dist/auth/recovery-job-repository.d.ts +219 -0
  77. package/dist/auth/recovery-job-repository.d.ts.map +1 -0
  78. package/dist/auth/recovery-job-repository.js +340 -0
  79. package/dist/auth/recovery-job-repository.js.map +1 -0
  80. package/dist/auth/recovery-service.d.ts +27 -0
  81. package/dist/auth/recovery-service.d.ts.map +1 -0
  82. package/dist/auth/recovery-service.js +53 -0
  83. package/dist/auth/recovery-service.js.map +1 -0
  84. package/dist/auth/recovery-worker.d.ts +158 -0
  85. package/dist/auth/recovery-worker.d.ts.map +1 -0
  86. package/dist/auth/recovery-worker.js +306 -0
  87. package/dist/auth/recovery-worker.js.map +1 -0
  88. package/dist/auth/template-engine.d.ts +134 -0
  89. package/dist/auth/template-engine.d.ts.map +1 -0
  90. package/dist/auth/template-engine.js +225 -0
  91. package/dist/auth/template-engine.js.map +1 -0
  92. package/dist/auth/token-repository.d.ts +238 -0
  93. package/dist/auth/token-repository.d.ts.map +1 -0
  94. package/dist/auth/token-repository.js +475 -0
  95. package/dist/auth/token-repository.js.map +1 -0
  96. package/dist/auth/tokens.d.ts +45 -0
  97. package/dist/auth/tokens.d.ts.map +1 -0
  98. package/dist/auth/tokens.js +51 -0
  99. package/dist/auth/tokens.js.map +1 -0
  100. package/dist/cli/bootstrap.d.ts +55 -0
  101. package/dist/cli/bootstrap.d.ts.map +1 -0
  102. package/dist/cli/bootstrap.js +90 -0
  103. package/dist/cli/bootstrap.js.map +1 -0
  104. package/dist/cli/commands/health.d.ts +20 -0
  105. package/dist/cli/commands/health.d.ts.map +1 -0
  106. package/dist/cli/commands/health.js +80 -0
  107. package/dist/cli/commands/health.js.map +1 -0
  108. package/dist/cli/commands/init.d.ts +48 -0
  109. package/dist/cli/commands/init.d.ts.map +1 -0
  110. package/dist/cli/commands/init.js +349 -0
  111. package/dist/cli/commands/init.js.map +1 -0
  112. package/dist/cli/commands/migrate.d.ts +19 -0
  113. package/dist/cli/commands/migrate.d.ts.map +1 -0
  114. package/dist/cli/commands/migrate.js +88 -0
  115. package/dist/cli/commands/migrate.js.map +1 -0
  116. package/dist/cli/commands/seed.d.ts +19 -0
  117. package/dist/cli/commands/seed.d.ts.map +1 -0
  118. package/dist/cli/commands/seed.js +71 -0
  119. package/dist/cli/commands/seed.js.map +1 -0
  120. package/dist/cli/commands/user-2fa.d.ts +29 -0
  121. package/dist/cli/commands/user-2fa.d.ts.map +1 -0
  122. package/dist/cli/commands/user-2fa.js +113 -0
  123. package/dist/cli/commands/user-2fa.js.map +1 -0
  124. package/dist/cli/error-handler.d.ts +31 -0
  125. package/dist/cli/error-handler.d.ts.map +1 -0
  126. package/dist/cli/error-handler.js +65 -0
  127. package/dist/cli/error-handler.js.map +1 -0
  128. package/dist/cli/index.d.ts +48 -0
  129. package/dist/cli/index.d.ts.map +1 -0
  130. package/dist/cli/index.js +99 -0
  131. package/dist/cli/index.js.map +1 -0
  132. package/dist/cli/output.d.ts +94 -0
  133. package/dist/cli/output.d.ts.map +1 -0
  134. package/dist/cli/output.js +124 -0
  135. package/dist/cli/output.js.map +1 -0
  136. package/dist/cli/prompt.d.ts +48 -0
  137. package/dist/cli/prompt.d.ts.map +1 -0
  138. package/dist/cli/prompt.js +96 -0
  139. package/dist/cli/prompt.js.map +1 -0
  140. package/dist/clients/cache.d.ts +63 -0
  141. package/dist/clients/cache.d.ts.map +1 -0
  142. package/dist/clients/cache.js +160 -0
  143. package/dist/clients/cache.js.map +1 -0
  144. package/dist/clients/crypto.d.ts +69 -0
  145. package/dist/clients/crypto.d.ts.map +1 -0
  146. package/dist/clients/crypto.js +83 -0
  147. package/dist/clients/crypto.js.map +1 -0
  148. package/dist/clients/errors.d.ts +27 -0
  149. package/dist/clients/errors.d.ts.map +1 -0
  150. package/dist/clients/errors.js +33 -0
  151. package/dist/clients/errors.js.map +1 -0
  152. package/dist/clients/index.d.ts +19 -0
  153. package/dist/clients/index.d.ts.map +1 -0
  154. package/dist/clients/index.js +23 -0
  155. package/dist/clients/index.js.map +1 -0
  156. package/dist/clients/repository.d.ts +161 -0
  157. package/dist/clients/repository.d.ts.map +1 -0
  158. package/dist/clients/repository.js +318 -0
  159. package/dist/clients/repository.js.map +1 -0
  160. package/dist/clients/resolve-login-methods.d.ts +74 -0
  161. package/dist/clients/resolve-login-methods.d.ts.map +1 -0
  162. package/dist/clients/resolve-login-methods.js +68 -0
  163. package/dist/clients/resolve-login-methods.js.map +1 -0
  164. package/dist/clients/secret-repository.d.ts +109 -0
  165. package/dist/clients/secret-repository.d.ts.map +1 -0
  166. package/dist/clients/secret-repository.js +166 -0
  167. package/dist/clients/secret-repository.js.map +1 -0
  168. package/dist/clients/secret-service.d.ts +87 -0
  169. package/dist/clients/secret-service.d.ts.map +1 -0
  170. package/dist/clients/secret-service.js +204 -0
  171. package/dist/clients/secret-service.js.map +1 -0
  172. package/dist/clients/service.d.ts +158 -0
  173. package/dist/clients/service.d.ts.map +1 -0
  174. package/dist/clients/service.js +523 -0
  175. package/dist/clients/service.js.map +1 -0
  176. package/dist/clients/types.d.ts +214 -0
  177. package/dist/clients/types.d.ts.map +1 -0
  178. package/dist/clients/types.js +77 -0
  179. package/dist/clients/types.js.map +1 -0
  180. package/dist/clients/validators.d.ts +87 -0
  181. package/dist/clients/validators.d.ts.map +1 -0
  182. package/dist/clients/validators.js +164 -0
  183. package/dist/clients/validators.js.map +1 -0
  184. package/dist/config/index.d.ts +23 -0
  185. package/dist/config/index.d.ts.map +1 -0
  186. package/dist/config/index.js +52 -0
  187. package/dist/config/index.js.map +1 -0
  188. package/dist/config/schema.d.ts +35 -0
  189. package/dist/config/schema.d.ts.map +1 -0
  190. package/dist/config/schema.js +167 -0
  191. package/dist/config/schema.js.map +1 -0
  192. package/dist/custom-claims/cache.d.ts +48 -0
  193. package/dist/custom-claims/cache.d.ts.map +1 -0
  194. package/dist/custom-claims/cache.js +122 -0
  195. package/dist/custom-claims/cache.js.map +1 -0
  196. package/dist/custom-claims/errors.d.ts +25 -0
  197. package/dist/custom-claims/errors.d.ts.map +1 -0
  198. package/dist/custom-claims/errors.js +31 -0
  199. package/dist/custom-claims/errors.js.map +1 -0
  200. package/dist/custom-claims/index.d.ts +14 -0
  201. package/dist/custom-claims/index.d.ts.map +1 -0
  202. package/dist/custom-claims/index.js +17 -0
  203. package/dist/custom-claims/index.js.map +1 -0
  204. package/dist/custom-claims/repository.d.ts +147 -0
  205. package/dist/custom-claims/repository.d.ts.map +1 -0
  206. package/dist/custom-claims/repository.js +338 -0
  207. package/dist/custom-claims/repository.js.map +1 -0
  208. package/dist/custom-claims/service.d.ts +129 -0
  209. package/dist/custom-claims/service.d.ts.map +1 -0
  210. package/dist/custom-claims/service.js +296 -0
  211. package/dist/custom-claims/service.js.map +1 -0
  212. package/dist/custom-claims/types.d.ts +132 -0
  213. package/dist/custom-claims/types.d.ts.map +1 -0
  214. package/dist/custom-claims/types.js +61 -0
  215. package/dist/custom-claims/types.js.map +1 -0
  216. package/dist/custom-claims/validators.d.ts +61 -0
  217. package/dist/custom-claims/validators.d.ts.map +1 -0
  218. package/dist/custom-claims/validators.js +136 -0
  219. package/dist/custom-claims/validators.js.map +1 -0
  220. package/dist/index.d.ts +14 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +91 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/lib/admin-permissions.d.ts +147 -0
  225. package/dist/lib/admin-permissions.d.ts.map +1 -0
  226. package/dist/lib/admin-permissions.js +261 -0
  227. package/dist/lib/admin-permissions.js.map +1 -0
  228. package/dist/lib/audit-log.d.ts +45 -0
  229. package/dist/lib/audit-log.d.ts.map +1 -0
  230. package/dist/lib/audit-log.js +79 -0
  231. package/dist/lib/audit-log.js.map +1 -0
  232. package/dist/lib/branding-assets.d.ts +47 -0
  233. package/dist/lib/branding-assets.d.ts.map +1 -0
  234. package/dist/lib/branding-assets.js +93 -0
  235. package/dist/lib/branding-assets.js.map +1 -0
  236. package/dist/lib/bulk-operations.d.ts +54 -0
  237. package/dist/lib/bulk-operations.d.ts.map +1 -0
  238. package/dist/lib/bulk-operations.js +201 -0
  239. package/dist/lib/bulk-operations.js.map +1 -0
  240. package/dist/lib/cursor.d.ts +110 -0
  241. package/dist/lib/cursor.d.ts.map +1 -0
  242. package/dist/lib/cursor.js +115 -0
  243. package/dist/lib/cursor.js.map +1 -0
  244. package/dist/lib/data-export.d.ts +67 -0
  245. package/dist/lib/data-export.d.ts.map +1 -0
  246. package/dist/lib/data-export.js +272 -0
  247. package/dist/lib/data-export.js.map +1 -0
  248. package/dist/lib/data-import-plan.d.ts +50 -0
  249. package/dist/lib/data-import-plan.d.ts.map +1 -0
  250. package/dist/lib/data-import-plan.js +235 -0
  251. package/dist/lib/data-import-plan.js.map +1 -0
  252. package/dist/lib/data-import.d.ts +276 -0
  253. package/dist/lib/data-import.d.ts.map +1 -0
  254. package/dist/lib/data-import.js +1694 -0
  255. package/dist/lib/data-import.js.map +1 -0
  256. package/dist/lib/database.d.ts +22 -0
  257. package/dist/lib/database.d.ts.map +1 -0
  258. package/dist/lib/database.js +103 -0
  259. package/dist/lib/database.js.map +1 -0
  260. package/dist/lib/entity-history.d.ts +52 -0
  261. package/dist/lib/entity-history.d.ts.map +1 -0
  262. package/dist/lib/entity-history.js +121 -0
  263. package/dist/lib/entity-history.js.map +1 -0
  264. package/dist/lib/etag.d.ts +92 -0
  265. package/dist/lib/etag.d.ts.map +1 -0
  266. package/dist/lib/etag.js +155 -0
  267. package/dist/lib/etag.js.map +1 -0
  268. package/dist/lib/image-validator.d.ts +44 -0
  269. package/dist/lib/image-validator.d.ts.map +1 -0
  270. package/dist/lib/image-validator.js +204 -0
  271. package/dist/lib/image-validator.js.map +1 -0
  272. package/dist/lib/logger.d.ts +15 -0
  273. package/dist/lib/logger.d.ts.map +1 -0
  274. package/dist/lib/logger.js +84 -0
  275. package/dist/lib/logger.js.map +1 -0
  276. package/dist/lib/migrator.d.ts +14 -0
  277. package/dist/lib/migrator.d.ts.map +1 -0
  278. package/dist/lib/migrator.js +34 -0
  279. package/dist/lib/migrator.js.map +1 -0
  280. package/dist/lib/redis.d.ts +5 -0
  281. package/dist/lib/redis.d.ts.map +1 -0
  282. package/dist/lib/redis.js +29 -0
  283. package/dist/lib/redis.js.map +1 -0
  284. package/dist/lib/runtime-paths.d.ts +38 -0
  285. package/dist/lib/runtime-paths.d.ts.map +1 -0
  286. package/dist/lib/runtime-paths.js +50 -0
  287. package/dist/lib/runtime-paths.js.map +1 -0
  288. package/dist/lib/session-tracking.d.ts +81 -0
  289. package/dist/lib/session-tracking.d.ts.map +1 -0
  290. package/dist/lib/session-tracking.js +145 -0
  291. package/dist/lib/session-tracking.js.map +1 -0
  292. package/dist/lib/signing-key-crypto.d.ts +53 -0
  293. package/dist/lib/signing-key-crypto.d.ts.map +1 -0
  294. package/dist/lib/signing-key-crypto.js +108 -0
  295. package/dist/lib/signing-key-crypto.js.map +1 -0
  296. package/dist/lib/signing-keys.d.ts +141 -0
  297. package/dist/lib/signing-keys.d.ts.map +1 -0
  298. package/dist/lib/signing-keys.js +206 -0
  299. package/dist/lib/signing-keys.js.map +1 -0
  300. package/dist/lib/stats.d.ts +83 -0
  301. package/dist/lib/stats.d.ts.map +1 -0
  302. package/dist/lib/stats.js +187 -0
  303. package/dist/lib/stats.js.map +1 -0
  304. package/dist/lib/super-admin-protection.d.ts +69 -0
  305. package/dist/lib/super-admin-protection.d.ts.map +1 -0
  306. package/dist/lib/super-admin-protection.js +102 -0
  307. package/dist/lib/super-admin-protection.js.map +1 -0
  308. package/dist/lib/system-config.d.ts +79 -0
  309. package/dist/lib/system-config.d.ts.map +1 -0
  310. package/dist/lib/system-config.js +159 -0
  311. package/dist/lib/system-config.js.map +1 -0
  312. package/dist/middleware/admin-auth.d.ts +100 -0
  313. package/dist/middleware/admin-auth.d.ts.map +1 -0
  314. package/dist/middleware/admin-auth.js +241 -0
  315. package/dist/middleware/admin-auth.js.map +1 -0
  316. package/dist/middleware/admin-cors.d.ts +41 -0
  317. package/dist/middleware/admin-cors.d.ts.map +1 -0
  318. package/dist/middleware/admin-cors.js +83 -0
  319. package/dist/middleware/admin-cors.js.map +1 -0
  320. package/dist/middleware/admin-mutation-audit.d.ts +9 -0
  321. package/dist/middleware/admin-mutation-audit.d.ts.map +1 -0
  322. package/dist/middleware/admin-mutation-audit.js +76 -0
  323. package/dist/middleware/admin-mutation-audit.js.map +1 -0
  324. package/dist/middleware/admin-rate-limiter.d.ts +56 -0
  325. package/dist/middleware/admin-rate-limiter.d.ts.map +1 -0
  326. package/dist/middleware/admin-rate-limiter.js +97 -0
  327. package/dist/middleware/admin-rate-limiter.js.map +1 -0
  328. package/dist/middleware/client-secret-hash.d.ts +34 -0
  329. package/dist/middleware/client-secret-hash.d.ts.map +1 -0
  330. package/dist/middleware/client-secret-hash.js +74 -0
  331. package/dist/middleware/client-secret-hash.js.map +1 -0
  332. package/dist/middleware/error-handler.d.ts +3 -0
  333. package/dist/middleware/error-handler.d.ts.map +1 -0
  334. package/dist/middleware/error-handler.js +93 -0
  335. package/dist/middleware/error-handler.js.map +1 -0
  336. package/dist/middleware/health.d.ts +3 -0
  337. package/dist/middleware/health.d.ts.map +1 -0
  338. package/dist/middleware/health.js +35 -0
  339. package/dist/middleware/health.js.map +1 -0
  340. package/dist/middleware/metrics.d.ts +40 -0
  341. package/dist/middleware/metrics.d.ts.map +1 -0
  342. package/dist/middleware/metrics.js +75 -0
  343. package/dist/middleware/metrics.js.map +1 -0
  344. package/dist/middleware/oidc-client-tenant.d.ts +37 -0
  345. package/dist/middleware/oidc-client-tenant.d.ts.map +1 -0
  346. package/dist/middleware/oidc-client-tenant.js +156 -0
  347. package/dist/middleware/oidc-client-tenant.js.map +1 -0
  348. package/dist/middleware/oidc-cors.d.ts +33 -0
  349. package/dist/middleware/oidc-cors.d.ts.map +1 -0
  350. package/dist/middleware/oidc-cors.js +68 -0
  351. package/dist/middleware/oidc-cors.js.map +1 -0
  352. package/dist/middleware/oidc-preflight-cors.d.ts +54 -0
  353. package/dist/middleware/oidc-preflight-cors.d.ts.map +1 -0
  354. package/dist/middleware/oidc-preflight-cors.js +267 -0
  355. package/dist/middleware/oidc-preflight-cors.js.map +1 -0
  356. package/dist/middleware/prompt-login-reset.d.ts +103 -0
  357. package/dist/middleware/prompt-login-reset.d.ts.map +1 -0
  358. package/dist/middleware/prompt-login-reset.js +204 -0
  359. package/dist/middleware/prompt-login-reset.js.map +1 -0
  360. package/dist/middleware/ready.d.ts +23 -0
  361. package/dist/middleware/ready.d.ts.map +1 -0
  362. package/dist/middleware/ready.js +60 -0
  363. package/dist/middleware/ready.js.map +1 -0
  364. package/dist/middleware/request-logger.d.ts +27 -0
  365. package/dist/middleware/request-logger.d.ts.map +1 -0
  366. package/dist/middleware/request-logger.js +46 -0
  367. package/dist/middleware/request-logger.js.map +1 -0
  368. package/dist/middleware/require-permission.d.ts +28 -0
  369. package/dist/middleware/require-permission.d.ts.map +1 -0
  370. package/dist/middleware/require-permission.js +63 -0
  371. package/dist/middleware/require-permission.js.map +1 -0
  372. package/dist/middleware/require-user-organization.d.ts +18 -0
  373. package/dist/middleware/require-user-organization.d.ts.map +1 -0
  374. package/dist/middleware/require-user-organization.js +53 -0
  375. package/dist/middleware/require-user-organization.js.map +1 -0
  376. package/dist/middleware/root-page.d.ts +62 -0
  377. package/dist/middleware/root-page.d.ts.map +1 -0
  378. package/dist/middleware/root-page.js +134 -0
  379. package/dist/middleware/root-page.js.map +1 -0
  380. package/dist/middleware/security-headers.d.ts +98 -0
  381. package/dist/middleware/security-headers.d.ts.map +1 -0
  382. package/dist/middleware/security-headers.js +159 -0
  383. package/dist/middleware/security-headers.js.map +1 -0
  384. package/dist/middleware/tenant-resolver.d.ts +37 -0
  385. package/dist/middleware/tenant-resolver.d.ts.map +1 -0
  386. package/dist/middleware/tenant-resolver.js +71 -0
  387. package/dist/middleware/tenant-resolver.js.map +1 -0
  388. package/dist/middleware/token-rate-limiter.d.ts +89 -0
  389. package/dist/middleware/token-rate-limiter.d.ts.map +1 -0
  390. package/dist/middleware/token-rate-limiter.js +189 -0
  391. package/dist/middleware/token-rate-limiter.js.map +1 -0
  392. package/dist/oidc/account-finder.d.ts +52 -0
  393. package/dist/oidc/account-finder.d.ts.map +1 -0
  394. package/dist/oidc/account-finder.js +134 -0
  395. package/dist/oidc/account-finder.js.map +1 -0
  396. package/dist/oidc/adapter-factory.d.ts +90 -0
  397. package/dist/oidc/adapter-factory.d.ts.map +1 -0
  398. package/dist/oidc/adapter-factory.js +165 -0
  399. package/dist/oidc/adapter-factory.js.map +1 -0
  400. package/dist/oidc/configuration.d.ts +155 -0
  401. package/dist/oidc/configuration.d.ts.map +1 -0
  402. package/dist/oidc/configuration.js +531 -0
  403. package/dist/oidc/configuration.js.map +1 -0
  404. package/dist/oidc/postgres-adapter.d.ts +159 -0
  405. package/dist/oidc/postgres-adapter.d.ts.map +1 -0
  406. package/dist/oidc/postgres-adapter.js +222 -0
  407. package/dist/oidc/postgres-adapter.js.map +1 -0
  408. package/dist/oidc/protocol-security-observer.d.ts +38 -0
  409. package/dist/oidc/protocol-security-observer.d.ts.map +1 -0
  410. package/dist/oidc/protocol-security-observer.js +93 -0
  411. package/dist/oidc/protocol-security-observer.js.map +1 -0
  412. package/dist/oidc/provider.d.ts +37 -0
  413. package/dist/oidc/provider.d.ts.map +1 -0
  414. package/dist/oidc/provider.js +71 -0
  415. package/dist/oidc/provider.js.map +1 -0
  416. package/dist/oidc/redis-adapter.d.ts +140 -0
  417. package/dist/oidc/redis-adapter.d.ts.map +1 -0
  418. package/dist/oidc/redis-adapter.js +344 -0
  419. package/dist/oidc/redis-adapter.js.map +1 -0
  420. package/dist/organizations/cache.d.ts +60 -0
  421. package/dist/organizations/cache.d.ts.map +1 -0
  422. package/dist/organizations/cache.js +157 -0
  423. package/dist/organizations/cache.js.map +1 -0
  424. package/dist/organizations/errors.d.ts +25 -0
  425. package/dist/organizations/errors.d.ts.map +1 -0
  426. package/dist/organizations/errors.js +31 -0
  427. package/dist/organizations/errors.js.map +1 -0
  428. package/dist/organizations/index.d.ts +15 -0
  429. package/dist/organizations/index.d.ts.map +1 -0
  430. package/dist/organizations/index.js +15 -0
  431. package/dist/organizations/index.js.map +1 -0
  432. package/dist/organizations/repository.d.ts +183 -0
  433. package/dist/organizations/repository.d.ts.map +1 -0
  434. package/dist/organizations/repository.js +354 -0
  435. package/dist/organizations/repository.js.map +1 -0
  436. package/dist/organizations/service.d.ts +168 -0
  437. package/dist/organizations/service.d.ts.map +1 -0
  438. package/dist/organizations/service.js +511 -0
  439. package/dist/organizations/service.js.map +1 -0
  440. package/dist/organizations/slugs.d.ts +57 -0
  441. package/dist/organizations/slugs.d.ts.map +1 -0
  442. package/dist/organizations/slugs.js +130 -0
  443. package/dist/organizations/slugs.js.map +1 -0
  444. package/dist/organizations/types.d.ts +146 -0
  445. package/dist/organizations/types.d.ts.map +1 -0
  446. package/dist/organizations/types.js +49 -0
  447. package/dist/organizations/types.js.map +1 -0
  448. package/dist/rbac/cache.d.ts +110 -0
  449. package/dist/rbac/cache.d.ts.map +1 -0
  450. package/dist/rbac/cache.js +271 -0
  451. package/dist/rbac/cache.js.map +1 -0
  452. package/dist/rbac/errors.d.ts +34 -0
  453. package/dist/rbac/errors.d.ts.map +1 -0
  454. package/dist/rbac/errors.js +43 -0
  455. package/dist/rbac/errors.js.map +1 -0
  456. package/dist/rbac/index.d.ts +17 -0
  457. package/dist/rbac/index.d.ts.map +1 -0
  458. package/dist/rbac/index.js +21 -0
  459. package/dist/rbac/index.js.map +1 -0
  460. package/dist/rbac/mapping-repository.d.ts +121 -0
  461. package/dist/rbac/mapping-repository.d.ts.map +1 -0
  462. package/dist/rbac/mapping-repository.js +224 -0
  463. package/dist/rbac/mapping-repository.js.map +1 -0
  464. package/dist/rbac/permission-repository.d.ts +100 -0
  465. package/dist/rbac/permission-repository.d.ts.map +1 -0
  466. package/dist/rbac/permission-repository.js +190 -0
  467. package/dist/rbac/permission-repository.js.map +1 -0
  468. package/dist/rbac/permission-service.d.ts +85 -0
  469. package/dist/rbac/permission-service.d.ts.map +1 -0
  470. package/dist/rbac/permission-service.js +179 -0
  471. package/dist/rbac/permission-service.js.map +1 -0
  472. package/dist/rbac/role-repository.d.ts +104 -0
  473. package/dist/rbac/role-repository.d.ts.map +1 -0
  474. package/dist/rbac/role-repository.js +202 -0
  475. package/dist/rbac/role-repository.js.map +1 -0
  476. package/dist/rbac/role-service.d.ts +111 -0
  477. package/dist/rbac/role-service.d.ts.map +1 -0
  478. package/dist/rbac/role-service.js +256 -0
  479. package/dist/rbac/role-service.js.map +1 -0
  480. package/dist/rbac/slugs.d.ts +116 -0
  481. package/dist/rbac/slugs.d.ts.map +1 -0
  482. package/dist/rbac/slugs.js +178 -0
  483. package/dist/rbac/slugs.js.map +1 -0
  484. package/dist/rbac/types.d.ts +187 -0
  485. package/dist/rbac/types.d.ts.map +1 -0
  486. package/dist/rbac/types.js +75 -0
  487. package/dist/rbac/types.js.map +1 -0
  488. package/dist/rbac/user-role-service.d.ts +101 -0
  489. package/dist/rbac/user-role-service.d.ts.map +1 -0
  490. package/dist/rbac/user-role-service.js +166 -0
  491. package/dist/rbac/user-role-service.js.map +1 -0
  492. package/dist/routes/applications.d.ts +40 -0
  493. package/dist/routes/applications.d.ts.map +1 -0
  494. package/dist/routes/applications.js +284 -0
  495. package/dist/routes/applications.js.map +1 -0
  496. package/dist/routes/audit.d.ts +31 -0
  497. package/dist/routes/audit.d.ts.map +1 -0
  498. package/dist/routes/audit.js +140 -0
  499. package/dist/routes/audit.js.map +1 -0
  500. package/dist/routes/branding.d.ts +17 -0
  501. package/dist/routes/branding.d.ts.map +1 -0
  502. package/dist/routes/branding.js +104 -0
  503. package/dist/routes/branding.js.map +1 -0
  504. package/dist/routes/bulk.d.ts +37 -0
  505. package/dist/routes/bulk.d.ts.map +1 -0
  506. package/dist/routes/bulk.js +94 -0
  507. package/dist/routes/bulk.js.map +1 -0
  508. package/dist/routes/clients.d.ts +44 -0
  509. package/dist/routes/clients.d.ts.map +1 -0
  510. package/dist/routes/clients.js +353 -0
  511. package/dist/routes/clients.js.map +1 -0
  512. package/dist/routes/config.d.ts +35 -0
  513. package/dist/routes/config.d.ts.map +1 -0
  514. package/dist/routes/config.js +115 -0
  515. package/dist/routes/config.js.map +1 -0
  516. package/dist/routes/custom-claims.d.ts +38 -0
  517. package/dist/routes/custom-claims.d.ts.map +1 -0
  518. package/dist/routes/custom-claims.js +208 -0
  519. package/dist/routes/custom-claims.js.map +1 -0
  520. package/dist/routes/exports.d.ts +21 -0
  521. package/dist/routes/exports.d.ts.map +1 -0
  522. package/dist/routes/exports.js +98 -0
  523. package/dist/routes/exports.js.map +1 -0
  524. package/dist/routes/imports.d.ts +18 -0
  525. package/dist/routes/imports.d.ts.map +1 -0
  526. package/dist/routes/imports.js +72 -0
  527. package/dist/routes/imports.js.map +1 -0
  528. package/dist/routes/interactions.d.ts +111 -0
  529. package/dist/routes/interactions.d.ts.map +1 -0
  530. package/dist/routes/interactions.js +1074 -0
  531. package/dist/routes/interactions.js.map +1 -0
  532. package/dist/routes/invitation.d.ts +31 -0
  533. package/dist/routes/invitation.d.ts.map +1 -0
  534. package/dist/routes/invitation.js +342 -0
  535. package/dist/routes/invitation.js.map +1 -0
  536. package/dist/routes/keys.d.ts +28 -0
  537. package/dist/routes/keys.d.ts.map +1 -0
  538. package/dist/routes/keys.js +91 -0
  539. package/dist/routes/keys.js.map +1 -0
  540. package/dist/routes/magic-link.d.ts +43 -0
  541. package/dist/routes/magic-link.d.ts.map +1 -0
  542. package/dist/routes/magic-link.js +264 -0
  543. package/dist/routes/magic-link.js.map +1 -0
  544. package/dist/routes/organizations.d.ts +38 -0
  545. package/dist/routes/organizations.d.ts.map +1 -0
  546. package/dist/routes/organizations.js +347 -0
  547. package/dist/routes/organizations.js.map +1 -0
  548. package/dist/routes/password-reset.d.ts +65 -0
  549. package/dist/routes/password-reset.d.ts.map +1 -0
  550. package/dist/routes/password-reset.js +493 -0
  551. package/dist/routes/password-reset.js.map +1 -0
  552. package/dist/routes/permissions.d.ts +33 -0
  553. package/dist/routes/permissions.d.ts.map +1 -0
  554. package/dist/routes/permissions.js +166 -0
  555. package/dist/routes/permissions.js.map +1 -0
  556. package/dist/routes/roles.d.ts +37 -0
  557. package/dist/routes/roles.d.ts.map +1 -0
  558. package/dist/routes/roles.js +222 -0
  559. package/dist/routes/roles.js.map +1 -0
  560. package/dist/routes/sessions.d.ts +37 -0
  561. package/dist/routes/sessions.d.ts.map +1 -0
  562. package/dist/routes/sessions.js +121 -0
  563. package/dist/routes/sessions.js.map +1 -0
  564. package/dist/routes/stats.d.ts +28 -0
  565. package/dist/routes/stats.d.ts.map +1 -0
  566. package/dist/routes/stats.js +65 -0
  567. package/dist/routes/stats.js.map +1 -0
  568. package/dist/routes/two-factor-admin.d.ts +54 -0
  569. package/dist/routes/two-factor-admin.d.ts.map +1 -0
  570. package/dist/routes/two-factor-admin.js +344 -0
  571. package/dist/routes/two-factor-admin.js.map +1 -0
  572. package/dist/routes/two-factor.d.ts +31 -0
  573. package/dist/routes/two-factor.d.ts.map +1 -0
  574. package/dist/routes/two-factor.js +595 -0
  575. package/dist/routes/two-factor.js.map +1 -0
  576. package/dist/routes/user-roles.d.ts +33 -0
  577. package/dist/routes/user-roles.d.ts.map +1 -0
  578. package/dist/routes/user-roles.js +129 -0
  579. package/dist/routes/user-roles.js.map +1 -0
  580. package/dist/routes/users.d.ts +61 -0
  581. package/dist/routes/users.d.ts.map +1 -0
  582. package/dist/routes/users.js +868 -0
  583. package/dist/routes/users.js.map +1 -0
  584. package/dist/security/decision-context.d.ts +52 -0
  585. package/dist/security/decision-context.d.ts.map +1 -0
  586. package/dist/security/decision-context.js +150 -0
  587. package/dist/security/decision-context.js.map +1 -0
  588. package/dist/security/decision-event.d.ts +128 -0
  589. package/dist/security/decision-event.d.ts.map +1 -0
  590. package/dist/security/decision-event.js +225 -0
  591. package/dist/security/decision-event.js.map +1 -0
  592. package/dist/security/transport-decision.d.ts +6 -0
  593. package/dist/security/transport-decision.d.ts.map +1 -0
  594. package/dist/security/transport-decision.js +37 -0
  595. package/dist/security/transport-decision.js.map +1 -0
  596. package/dist/server.d.ts +50 -0
  597. package/dist/server.d.ts.map +1 -0
  598. package/dist/server.js +491 -0
  599. package/dist/server.js.map +1 -0
  600. package/dist/two-factor/cache.d.ts +47 -0
  601. package/dist/two-factor/cache.d.ts.map +1 -0
  602. package/dist/two-factor/cache.js +122 -0
  603. package/dist/two-factor/cache.js.map +1 -0
  604. package/dist/two-factor/crypto.d.ts +41 -0
  605. package/dist/two-factor/crypto.d.ts.map +1 -0
  606. package/dist/two-factor/crypto.js +94 -0
  607. package/dist/two-factor/crypto.js.map +1 -0
  608. package/dist/two-factor/errors.d.ts +82 -0
  609. package/dist/two-factor/errors.d.ts.map +1 -0
  610. package/dist/two-factor/errors.js +112 -0
  611. package/dist/two-factor/errors.js.map +1 -0
  612. package/dist/two-factor/index.d.ts +18 -0
  613. package/dist/two-factor/index.d.ts.map +1 -0
  614. package/dist/two-factor/index.js +25 -0
  615. package/dist/two-factor/index.js.map +1 -0
  616. package/dist/two-factor/otp.d.ts +43 -0
  617. package/dist/two-factor/otp.d.ts.map +1 -0
  618. package/dist/two-factor/otp.js +57 -0
  619. package/dist/two-factor/otp.js.map +1 -0
  620. package/dist/two-factor/recovery.d.ts +45 -0
  621. package/dist/two-factor/recovery.d.ts.map +1 -0
  622. package/dist/two-factor/recovery.js +104 -0
  623. package/dist/two-factor/recovery.js.map +1 -0
  624. package/dist/two-factor/repository.d.ts +156 -0
  625. package/dist/two-factor/repository.d.ts.map +1 -0
  626. package/dist/two-factor/repository.js +251 -0
  627. package/dist/two-factor/repository.js.map +1 -0
  628. package/dist/two-factor/service.d.ts +232 -0
  629. package/dist/two-factor/service.d.ts.map +1 -0
  630. package/dist/two-factor/service.js +599 -0
  631. package/dist/two-factor/service.js.map +1 -0
  632. package/dist/two-factor/totp.d.ts +57 -0
  633. package/dist/two-factor/totp.d.ts.map +1 -0
  634. package/dist/two-factor/totp.js +96 -0
  635. package/dist/two-factor/totp.js.map +1 -0
  636. package/dist/two-factor/types.d.ts +152 -0
  637. package/dist/two-factor/types.d.ts.map +1 -0
  638. package/dist/two-factor/types.js +64 -0
  639. package/dist/two-factor/types.js.map +1 -0
  640. package/dist/users/cache.d.ts +49 -0
  641. package/dist/users/cache.d.ts.map +1 -0
  642. package/dist/users/cache.js +140 -0
  643. package/dist/users/cache.js.map +1 -0
  644. package/dist/users/claims.d.ts +67 -0
  645. package/dist/users/claims.d.ts.map +1 -0
  646. package/dist/users/claims.js +104 -0
  647. package/dist/users/claims.js.map +1 -0
  648. package/dist/users/errors.d.ts +25 -0
  649. package/dist/users/errors.d.ts.map +1 -0
  650. package/dist/users/errors.js +31 -0
  651. package/dist/users/errors.js.map +1 -0
  652. package/dist/users/gdpr.d.ts +110 -0
  653. package/dist/users/gdpr.d.ts.map +1 -0
  654. package/dist/users/gdpr.js +211 -0
  655. package/dist/users/gdpr.js.map +1 -0
  656. package/dist/users/index.d.ts +20 -0
  657. package/dist/users/index.d.ts.map +1 -0
  658. package/dist/users/index.js +19 -0
  659. package/dist/users/index.js.map +1 -0
  660. package/dist/users/password.d.ts +85 -0
  661. package/dist/users/password.d.ts.map +1 -0
  662. package/dist/users/password.js +138 -0
  663. package/dist/users/password.js.map +1 -0
  664. package/dist/users/repository.d.ts +263 -0
  665. package/dist/users/repository.d.ts.map +1 -0
  666. package/dist/users/repository.js +499 -0
  667. package/dist/users/repository.js.map +1 -0
  668. package/dist/users/service.d.ts +299 -0
  669. package/dist/users/service.d.ts.map +1 -0
  670. package/dist/users/service.js +698 -0
  671. package/dist/users/service.js.map +1 -0
  672. package/dist/users/types.d.ts +220 -0
  673. package/dist/users/types.d.ts.map +1 -0
  674. package/dist/users/types.js +77 -0
  675. package/dist/users/types.js.map +1 -0
  676. package/locales/default/en/common.json +10 -0
  677. package/locales/default/en/consent.json +13 -0
  678. package/locales/default/en/emails.json +17 -0
  679. package/locales/default/en/errors.json +21 -0
  680. package/locales/default/en/forgot-password.json +11 -0
  681. package/locales/default/en/invitation.json +18 -0
  682. package/locales/default/en/login.json +17 -0
  683. package/locales/default/en/logout.json +9 -0
  684. package/locales/default/en/magic-link.json +12 -0
  685. package/locales/default/en/reset-password.json +16 -0
  686. package/locales/default/en/two-factor.json +29 -0
  687. package/migrations/.gitkeep +0 -0
  688. package/migrations/001_extensions.sql +22 -0
  689. package/migrations/002_organizations.sql +43 -0
  690. package/migrations/003_applications.sql +50 -0
  691. package/migrations/004_clients.sql +74 -0
  692. package/migrations/005_users.sql +119 -0
  693. package/migrations/006_roles_permissions.sql +80 -0
  694. package/migrations/007_custom_claims.sql +55 -0
  695. package/migrations/008_config.sql +51 -0
  696. package/migrations/009_audit_log.sql +31 -0
  697. package/migrations/010_oidc_adapter.sql +34 -0
  698. package/migrations/011_seed.sql +53 -0
  699. package/migrations/012_two_factor.sql +88 -0
  700. package/migrations/013_client_secret_sha256.sql +22 -0
  701. package/migrations/014_login_methods.sql +34 -0
  702. package/migrations/015_signing_key_encryption.sql +20 -0
  703. package/migrations/016_failed_login_count.sql +27 -0
  704. package/migrations/017_audit_retention.sql +11 -0
  705. package/migrations/018_admin_api_enhancements.sql +78 -0
  706. package/migrations/019_invitation_details.sql +27 -0
  707. package/migrations/020_two_factor_admin_permission.sql +108 -0
  708. package/migrations/021_auth_recovery_jobs.sql +74 -0
  709. package/migrations/022_recovery_artifact_ownership.sql +25 -0
  710. package/migrations/023_magic_link_authority.sql +39 -0
  711. package/package.json +106 -0
  712. package/templates/default/emails/invitation.hbs +39 -0
  713. package/templates/default/emails/invitation.txt.hbs +18 -0
  714. package/templates/default/emails/magic-link.hbs +30 -0
  715. package/templates/default/emails/magic-link.txt.hbs +11 -0
  716. package/templates/default/emails/otp-code.hbs +31 -0
  717. package/templates/default/emails/otp-code.txt.hbs +15 -0
  718. package/templates/default/emails/password-changed.hbs +24 -0
  719. package/templates/default/emails/password-changed.txt.hbs +7 -0
  720. package/templates/default/emails/password-reset.hbs +30 -0
  721. package/templates/default/emails/password-reset.txt.hbs +11 -0
  722. package/templates/default/emails/welcome.hbs +25 -0
  723. package/templates/default/emails/welcome.txt.hbs +9 -0
  724. package/templates/default/layouts/main.hbs +49 -0
  725. package/templates/default/pages/accept-invite.hbs +25 -0
  726. package/templates/default/pages/consent.hbs +27 -0
  727. package/templates/default/pages/error.hbs +15 -0
  728. package/templates/default/pages/forgot-password.hbs +21 -0
  729. package/templates/default/pages/invite-expired.hbs +6 -0
  730. package/templates/default/pages/invite-success.hbs +9 -0
  731. package/templates/default/pages/login.hbs +108 -0
  732. package/templates/default/pages/logout-success.hbs +5 -0
  733. package/templates/default/pages/logout.hbs +24 -0
  734. package/templates/default/pages/magic-link-sent.hbs +10 -0
  735. package/templates/default/pages/magic-link-success.hbs +17 -0
  736. package/templates/default/pages/reset-password.hbs +20 -0
  737. package/templates/default/pages/reset-success.hbs +9 -0
  738. package/templates/default/pages/two-factor-setup.hbs +65 -0
  739. package/templates/default/pages/two-factor-verify.hbs +77 -0
  740. package/templates/default/partials/flash-messages.hbs +10 -0
  741. package/templates/default/partials/footer.hbs +4 -0
  742. package/templates/default/partials/header.hbs +8 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Tenant-safe bulk status operations with ordered partial results.
3
+ *
4
+ * Whole-request validation completes before the first mutation. Each admitted item then owns one
5
+ * database transaction containing its lock, transition, update, and durable audit record. Domain
6
+ * rejections are isolated to that item. Dependency failures stop the operation and mark the
7
+ * current and remaining items as not attempted.
8
+ */
9
+ import { randomUUID } from 'node:crypto';
10
+ import { getPool } from './database.js';
11
+ import { logger } from './logger.js';
12
+ import { writeAuditLogInTransaction } from './audit-log.js';
13
+ const ORGANIZATION_TRANSITIONS = Object.freeze({
14
+ activate: { from: ['suspended'], to: 'active' },
15
+ suspend: { from: ['active'], to: 'suspended' },
16
+ archive: { from: ['active', 'suspended'], to: 'archived' },
17
+ });
18
+ const USER_TRANSITIONS = Object.freeze({
19
+ activate: { from: ['inactive', 'suspended'], to: 'active' },
20
+ deactivate: { from: ['active'], to: 'inactive' },
21
+ suspend: { from: ['active'], to: 'suspended' },
22
+ lock: { from: ['active'], to: 'locked' },
23
+ unlock: { from: ['locked'], to: 'active' },
24
+ });
25
+ const MAXIMUM_ITEMS = 100;
26
+ const MAXIMUM_REASON_CHARACTERS = 500;
27
+ /** Validate every request-level invariant before opening an item transaction. */
28
+ function requireValidInput(input) {
29
+ if (input.entityIds.length === 0)
30
+ return undefined;
31
+ if (input.entityIds.length > MAXIMUM_ITEMS) {
32
+ throw new Error('Bulk operations are limited to 100 items at a time');
33
+ }
34
+ if (new Set(input.entityIds).size !== input.entityIds.length) {
35
+ throw new Error('Bulk operations require unique entity identifiers');
36
+ }
37
+ if (input.reason !== undefined && input.reason.length > MAXIMUM_REASON_CHARACTERS) {
38
+ throw new Error('Bulk operation reason is limited to 500 characters');
39
+ }
40
+ if (input.entityType === 'user' && !input.organizationId) {
41
+ throw new Error('Organization scope is required for user bulk operations');
42
+ }
43
+ const transitions = input.entityType === 'organization' ? ORGANIZATION_TRANSITIONS : USER_TRANSITIONS;
44
+ const transition = transitions[input.action];
45
+ if (!transition) {
46
+ throw new Error(`Invalid action '${input.action}' for entity type '${input.entityType}'`);
47
+ }
48
+ return transition;
49
+ }
50
+ /** Roll back an open item transaction before returning a domain rejection. */
51
+ async function rollback(client) {
52
+ await client.query('ROLLBACK');
53
+ }
54
+ /** Build the tenant-qualified row lock for the closed entity catalog. */
55
+ async function lockCurrentStatus(client, input, id) {
56
+ if (input.entityType === 'user') {
57
+ const result = await client.query(`SELECT status FROM users
58
+ WHERE id = $1 AND organization_id = $2
59
+ FOR UPDATE`, [id, input.organizationId]);
60
+ return result.rows[0]?.status;
61
+ }
62
+ const result = await client.query('SELECT status FROM organizations WHERE id = $1 FOR UPDATE', [id]);
63
+ return result.rows[0]?.status;
64
+ }
65
+ /** Persist the status transition using only closed table and field choices. */
66
+ async function updateStatus(client, input, id, nextStatus) {
67
+ if (input.entityType === 'user' &&
68
+ input.reason &&
69
+ (input.action === 'suspend' || input.action === 'lock')) {
70
+ await client.query(`UPDATE users
71
+ SET status = $1, updated_at = NOW(), suspension_reason = $2
72
+ WHERE id = $3 AND organization_id = $4`, [nextStatus, input.reason, id, input.organizationId]);
73
+ return;
74
+ }
75
+ if (input.entityType === 'user') {
76
+ await client.query(`UPDATE users SET status = $1, updated_at = NOW()
77
+ WHERE id = $2 AND organization_id = $3`, [nextStatus, id, input.organizationId]);
78
+ return;
79
+ }
80
+ await client.query('UPDATE organizations SET status = $1, updated_at = NOW() WHERE id = $2', [
81
+ nextStatus,
82
+ id,
83
+ ]);
84
+ }
85
+ /** Insert the per-item audit record through the same transaction as the mutation. */
86
+ async function insertAudit(client, input, id, previousStatus, nextStatus) {
87
+ const organizationId = input.entityType === 'organization' ? id : input.organizationId;
88
+ const userId = input.entityType === 'user' ? id : null;
89
+ await writeAuditLogInTransaction(client, {
90
+ organizationId,
91
+ ...(userId === null ? {} : { userId }),
92
+ ...(input.actorId === undefined ? {} : { actorId: input.actorId }),
93
+ eventType: `admin.bulk.${input.entityType}.status_changed`,
94
+ eventCategory: 'admin',
95
+ metadata: { action: input.action, previousStatus, newStatus: nextStatus },
96
+ });
97
+ }
98
+ /** Create one concealed missing/foreign item result. */
99
+ function concealedResult(id) {
100
+ return {
101
+ id,
102
+ success: false,
103
+ outcome: 'failed',
104
+ code: 'not_found_or_not_authorized',
105
+ error: 'Entity not found or not authorized',
106
+ };
107
+ }
108
+ /** Create one closed invalid-transition result. */
109
+ function invalidTransitionResult(id, previousStatus) {
110
+ return {
111
+ id,
112
+ success: false,
113
+ outcome: 'failed',
114
+ code: 'invalid_transition',
115
+ error: 'Status transition not allowed',
116
+ previousStatus,
117
+ };
118
+ }
119
+ /** Create an explicit result for work skipped after dependency failure. */
120
+ function notAttemptedResult(id) {
121
+ return {
122
+ id,
123
+ success: false,
124
+ outcome: 'not_attempted',
125
+ code: 'not_attempted',
126
+ error: 'Operation not attempted',
127
+ };
128
+ }
129
+ /**
130
+ * Execute an ordered partial-result bulk status operation.
131
+ *
132
+ * @param input - Fully validated entity, action, scope, and actor input.
133
+ * @returns Ordered item outcomes. A dependency stop includes one server correlation ID.
134
+ */
135
+ export async function bulkStatusChange(input) {
136
+ const transition = requireValidInput(input);
137
+ if (!transition) {
138
+ return { total: 0, succeeded: 0, failed: 0, results: [] };
139
+ }
140
+ const pool = getPool();
141
+ const results = [];
142
+ let correlationId;
143
+ for (let index = 0; index < input.entityIds.length; index += 1) {
144
+ const id = input.entityIds[index];
145
+ let client;
146
+ try {
147
+ client = await pool.connect();
148
+ await client.query('BEGIN');
149
+ const previousStatus = await lockCurrentStatus(client, input, id);
150
+ if (previousStatus === undefined) {
151
+ await rollback(client);
152
+ results.push(concealedResult(id));
153
+ continue;
154
+ }
155
+ if (!transition.from.includes(previousStatus)) {
156
+ await rollback(client);
157
+ results.push(invalidTransitionResult(id, previousStatus));
158
+ continue;
159
+ }
160
+ await updateStatus(client, input, id, transition.to);
161
+ await insertAudit(client, input, id, previousStatus, transition.to);
162
+ await client.query('COMMIT');
163
+ results.push({
164
+ id,
165
+ success: true,
166
+ outcome: 'succeeded',
167
+ code: null,
168
+ previousStatus,
169
+ newStatus: transition.to,
170
+ });
171
+ }
172
+ catch {
173
+ if (client) {
174
+ try {
175
+ await rollback(client);
176
+ }
177
+ catch {
178
+ // The public result remains the same when rollback outcome is unknown.
179
+ }
180
+ }
181
+ correlationId = randomUUID();
182
+ logger.warn({ correlationId, entityType: input.entityType, action: input.action }, 'Bulk operation stopped after a dependency failure');
183
+ for (let pending = index; pending < input.entityIds.length; pending += 1) {
184
+ results.push(notAttemptedResult(input.entityIds[pending]));
185
+ }
186
+ break;
187
+ }
188
+ finally {
189
+ client?.release();
190
+ }
191
+ }
192
+ const succeeded = results.filter((result) => result.outcome === 'succeeded').length;
193
+ return {
194
+ total: input.entityIds.length,
195
+ succeeded,
196
+ failed: input.entityIds.length - succeeded,
197
+ results,
198
+ ...(correlationId ? { correlationId } : {}),
199
+ };
200
+ }
201
+ //# sourceMappingURL=bulk-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-operations.js","sourceRoot":"","sources":["../../src/lib/bulk-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Cursor-based pagination utility.
3
+ *
4
+ * Provides opaque cursor encoding and decoding for keyset pagination.
5
+ * Cursors encode the sort value and entity ID of the last item in a
6
+ * result set, enabling efficient "next page" queries using:
7
+ *
8
+ * WHERE (sort_column, id) > ($cursor_sort, $cursor_id)
9
+ *
10
+ * Cursors are base64url-encoded JSON strings — opaque to API consumers
11
+ * but decodable by the server. Invalid or tampered cursors are safely
12
+ * rejected (returns null from decodeCursor).
13
+ *
14
+ * @module lib/cursor
15
+ */
16
+ /**
17
+ * Internal cursor payload — the data encoded inside an opaque cursor string.
18
+ *
19
+ * @property s - Sort field value at the cursor position (string, number, or null for NULLs)
20
+ * @property i - Entity ID at the cursor position (UUID) — ensures stable ordering for ties
21
+ */
22
+ export interface CursorPayload {
23
+ /** Sort field value at the cursor position */
24
+ s: string | number | null;
25
+ /** Entity ID (UUID) — tie-breaker for stable ordering */
26
+ i: string;
27
+ }
28
+ /**
29
+ * Options for cursor-based pagination.
30
+ *
31
+ * When `cursor` is provided, keyset pagination is used.
32
+ * When `page` is provided (without cursor), offset pagination is used.
33
+ * When neither is provided, defaults to offset pagination page 1.
34
+ */
35
+ export interface CursorPaginationOptions {
36
+ /** Opaque cursor string from a previous response */
37
+ cursor?: string;
38
+ /** Maximum items per page (default 25, max 100) */
39
+ limit?: number;
40
+ /** Sort column (must be whitelisted per-entity) */
41
+ sortBy?: string;
42
+ /** Sort direction */
43
+ sortOrder?: 'asc' | 'desc';
44
+ }
45
+ /**
46
+ * Result shape for cursor-paginated queries.
47
+ *
48
+ * Includes the data array, navigation cursors, and a hasMore flag.
49
+ * `nextCursor` is null when there are no more results.
50
+ * `previousCursor` is the cursor of the first item (for potential reverse navigation).
51
+ */
52
+ export interface CursorPaginatedResult<T> {
53
+ /** The page of results */
54
+ data: T[];
55
+ /** Cursor to fetch the next page — null if no more results */
56
+ nextCursor: string | null;
57
+ /** Cursor of the first item in this page — null if the page is empty */
58
+ previousCursor: string | null;
59
+ /** Whether there are more results after this page */
60
+ hasMore: boolean;
61
+ }
62
+ /**
63
+ * Encode a cursor from the last item in a result set.
64
+ *
65
+ * Produces an opaque base64url string safe for URL query parameters.
66
+ * The cursor encodes the sort value and entity ID, which together
67
+ * define the exact position in the ordered result set.
68
+ *
69
+ * @param sortValue - The sort field value of the item (string, number, or null)
70
+ * @param id - The entity UUID of the item
71
+ * @returns Opaque cursor string
72
+ *
73
+ * @example
74
+ * const cursor = encodeCursor('2026-01-15T10:00:00Z', 'abc-123');
75
+ * // → 'eyJzIjoiMjAyNi0wMS0xNVQxMDowMDowMFoiLCJpIjoiYWJjLTEyMyJ9'
76
+ */
77
+ export declare function encodeCursor(sortValue: string | number | null, id: string): string;
78
+ /**
79
+ * Decode an opaque cursor string back to a CursorPayload.
80
+ *
81
+ * Returns null if the cursor is invalid — malformed base64, invalid JSON,
82
+ * missing fields, or wrong types. This ensures tampered or corrupted
83
+ * cursors are safely rejected without throwing exceptions.
84
+ *
85
+ * @param cursor - The opaque cursor string to decode
86
+ * @returns Decoded payload, or null if invalid
87
+ *
88
+ * @example
89
+ * const payload = decodeCursor('eyJzIjoiMjAyNi0wMS0xNVQxMDowMDowMFoiLCJpIjoiYWJjLTEyMyJ9');
90
+ * // → { s: '2026-01-15T10:00:00Z', i: 'abc-123' }
91
+ *
92
+ * const invalid = decodeCursor('not-a-cursor');
93
+ * // → null
94
+ */
95
+ export declare function decodeCursor(cursor: string): CursorPayload | null;
96
+ /**
97
+ * Build a CursorPaginatedResult from a query result set.
98
+ *
99
+ * The caller should query for `limit + 1` rows. If the extra row exists,
100
+ * `hasMore` is true and the extra row is stripped from the result.
101
+ * Cursors are generated using the provided sort value extractor function.
102
+ *
103
+ * @param rows - Raw query rows (may include the extra hasMore row)
104
+ * @param limit - The requested page size
105
+ * @param getSortValue - Function to extract the sort value from a row
106
+ * @param getId - Function to extract the entity ID from a row
107
+ * @returns CursorPaginatedResult with cursors and hasMore flag
108
+ */
109
+ export declare function buildCursorResult<T>(rows: T[], limit: number, getSortValue: (row: T) => string | number | null, getId: (row: T) => string): CursorPaginatedResult<T>;
110
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/lib/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,8CAA8C;IAC9C,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC1B,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,0BAA0B;IAC1B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wEAAwE;IACxE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;CAClB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAKlF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAoCjE;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,IAAI,EAAE,CAAC,EAAE,EACT,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,GAAG,IAAI,EAChD,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GACxB,qBAAqB,CAAC,CAAC,CAAC,CAa1B"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Cursor-based pagination utility.
3
+ *
4
+ * Provides opaque cursor encoding and decoding for keyset pagination.
5
+ * Cursors encode the sort value and entity ID of the last item in a
6
+ * result set, enabling efficient "next page" queries using:
7
+ *
8
+ * WHERE (sort_column, id) > ($cursor_sort, $cursor_id)
9
+ *
10
+ * Cursors are base64url-encoded JSON strings — opaque to API consumers
11
+ * but decodable by the server. Invalid or tampered cursors are safely
12
+ * rejected (returns null from decodeCursor).
13
+ *
14
+ * @module lib/cursor
15
+ */
16
+ // ============================================================================
17
+ // Encoding / Decoding
18
+ // ============================================================================
19
+ /**
20
+ * Encode a cursor from the last item in a result set.
21
+ *
22
+ * Produces an opaque base64url string safe for URL query parameters.
23
+ * The cursor encodes the sort value and entity ID, which together
24
+ * define the exact position in the ordered result set.
25
+ *
26
+ * @param sortValue - The sort field value of the item (string, number, or null)
27
+ * @param id - The entity UUID of the item
28
+ * @returns Opaque cursor string
29
+ *
30
+ * @example
31
+ * const cursor = encodeCursor('2026-01-15T10:00:00Z', 'abc-123');
32
+ * // → 'eyJzIjoiMjAyNi0wMS0xNVQxMDowMDowMFoiLCJpIjoiYWJjLTEyMyJ9'
33
+ */
34
+ export function encodeCursor(sortValue, id) {
35
+ const payload = { s: sortValue, i: id };
36
+ const json = JSON.stringify(payload);
37
+ // Use base64url encoding (no padding) for URL safety
38
+ return Buffer.from(json, 'utf-8').toString('base64url');
39
+ }
40
+ /**
41
+ * Decode an opaque cursor string back to a CursorPayload.
42
+ *
43
+ * Returns null if the cursor is invalid — malformed base64, invalid JSON,
44
+ * missing fields, or wrong types. This ensures tampered or corrupted
45
+ * cursors are safely rejected without throwing exceptions.
46
+ *
47
+ * @param cursor - The opaque cursor string to decode
48
+ * @returns Decoded payload, or null if invalid
49
+ *
50
+ * @example
51
+ * const payload = decodeCursor('eyJzIjoiMjAyNi0wMS0xNVQxMDowMDowMFoiLCJpIjoiYWJjLTEyMyJ9');
52
+ * // → { s: '2026-01-15T10:00:00Z', i: 'abc-123' }
53
+ *
54
+ * const invalid = decodeCursor('not-a-cursor');
55
+ * // → null
56
+ */
57
+ export function decodeCursor(cursor) {
58
+ try {
59
+ const json = Buffer.from(cursor, 'base64url').toString('utf-8');
60
+ const parsed = JSON.parse(json);
61
+ // Validate shape: must be an object with 's' and 'i' fields
62
+ if (typeof parsed !== 'object' ||
63
+ parsed === null ||
64
+ !('s' in parsed) ||
65
+ !('i' in parsed)) {
66
+ return null;
67
+ }
68
+ const obj = parsed;
69
+ // Validate 'i' is a non-empty string (entity ID)
70
+ if (typeof obj.i !== 'string' || obj.i.length === 0) {
71
+ return null;
72
+ }
73
+ // Validate 's' is string, number, or null (sort value)
74
+ const sortType = typeof obj.s;
75
+ if (obj.s !== null && sortType !== 'string' && sortType !== 'number') {
76
+ return null;
77
+ }
78
+ return {
79
+ s: obj.s,
80
+ i: obj.i,
81
+ };
82
+ }
83
+ catch {
84
+ // JSON parse error, base64 decode error, etc.
85
+ return null;
86
+ }
87
+ }
88
+ // ============================================================================
89
+ // Result Builder
90
+ // ============================================================================
91
+ /**
92
+ * Build a CursorPaginatedResult from a query result set.
93
+ *
94
+ * The caller should query for `limit + 1` rows. If the extra row exists,
95
+ * `hasMore` is true and the extra row is stripped from the result.
96
+ * Cursors are generated using the provided sort value extractor function.
97
+ *
98
+ * @param rows - Raw query rows (may include the extra hasMore row)
99
+ * @param limit - The requested page size
100
+ * @param getSortValue - Function to extract the sort value from a row
101
+ * @param getId - Function to extract the entity ID from a row
102
+ * @returns CursorPaginatedResult with cursors and hasMore flag
103
+ */
104
+ export function buildCursorResult(rows, limit, getSortValue, getId) {
105
+ const hasMore = rows.length > limit;
106
+ const data = hasMore ? rows.slice(0, limit) : rows;
107
+ const nextCursor = hasMore && data.length > 0
108
+ ? encodeCursor(getSortValue(data[data.length - 1]), getId(data[data.length - 1]))
109
+ : null;
110
+ const previousCursor = data.length > 0
111
+ ? encodeCursor(getSortValue(data[0]), getId(data[0]))
112
+ : null;
113
+ return { data, nextCursor, previousCursor, hasMore };
114
+ }
115
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/lib/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Bounded administrative data export.
3
+ *
4
+ * Every query selects an explicit public field list, reads one row beyond the public limit, and
5
+ * writes a content-free audit record in the same repeatable-read transaction. CSV output is
6
+ * neutralized before quoting so spreadsheet applications cannot execute imported formulas.
7
+ */
8
+ /** Closed serialization formats accepted by the export service. */
9
+ export type ExportFormat = 'json' | 'csv';
10
+ /** Closed entity catalog accepted by the export service. */
11
+ export type ExportEntityType = 'users' | 'organizations' | 'clients' | 'roles' | 'audit';
12
+ /** Maximum number of rows returned by one export. */
13
+ export declare const MAXIMUM_EXPORT_ROWS = 10000;
14
+ /** Input for one authorized administrative export. */
15
+ export interface ExportOptions {
16
+ /** Entity catalog to serialize. */
17
+ entityType: ExportEntityType;
18
+ /** Public output representation. */
19
+ format: ExportFormat;
20
+ /** Exact tenant scope for tenant-owned data. */
21
+ organizationId?: string;
22
+ /** Exact application relationship required for role data. */
23
+ applicationId?: string;
24
+ /** Inclusive audit-window start. */
25
+ startDate?: Date;
26
+ /** Inclusive audit-window end. */
27
+ endDate?: Date;
28
+ /** Authenticated administrator recorded without export content. */
29
+ actorId?: string;
30
+ }
31
+ /** Serialized export admitted after scope, bound, and audit checks. */
32
+ export interface ExportResult {
33
+ /** Complete serialized document. */
34
+ data: string;
35
+ /** Public response media type. */
36
+ contentType: string;
37
+ /** Attachment filename without user-controlled input. */
38
+ filename: string;
39
+ /** Exact number of exported rows. */
40
+ rowCount: number;
41
+ }
42
+ /** Stable public export failures. */
43
+ export type ExportFailureCode = 'export_scope_required' | 'export_invalid_range' | 'export_too_large' | 'export_failed';
44
+ /** Minimal export failure which never retains query or row diagnostics. */
45
+ export declare class ExportOperationError extends Error {
46
+ /** Stable public failure discriminator. */
47
+ readonly code: ExportFailureCode;
48
+ /** Recommended HTTP status. */
49
+ readonly status: 400 | 413 | 503;
50
+ /**
51
+ * Create a closed export failure.
52
+ *
53
+ * @param code - Stable public failure discriminator.
54
+ * @param status - Recommended route status.
55
+ */
56
+ constructor(code: ExportFailureCode, status: 400 | 413 | 503);
57
+ }
58
+ /**
59
+ * Export one authorized entity set through a bounded repeatable-read transaction.
60
+ *
61
+ * @param options - Exact entity, representation, scope, and actor.
62
+ * @returns Complete serialized export and exact row count.
63
+ * @throws ExportOperationError when scope is incomplete, the bound is exceeded, or persistence
64
+ * fails.
65
+ */
66
+ export declare function exportData(options: ExportOptions): Promise<ExportResult>;
67
+ //# sourceMappingURL=data-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-export.d.ts","sourceRoot":"","sources":["../../src/lib/data-export.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1C,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAE1C,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC5B,mCAAmC;IACnC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,oCAAoC;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,kCAAkC;IAClC,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAC3B,uBAAuB,GAAG,sBAAsB,GAAG,kBAAkB,GAAG,eAAe,CAAC;AAE1F,2EAA2E;AAC3E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,2CAA2C;IAC3C,SAAgB,IAAI,EAAE,iBAAiB,CAAC;IAExC,+BAA+B;IAC/B,SAAgB,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAExC;;;;;OAKG;IACH,YAAmB,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAKlE;CACF;AAqMD;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAgD9E"}