@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,256 @@
1
+ /**
2
+ * Role service — business logic for role management.
3
+ *
4
+ * Orchestrates role CRUD operations with slug validation, uniqueness
5
+ * checks, deletion guards, cache management, and audit logging.
6
+ * Also handles role-permission assignment operations.
7
+ *
8
+ * All write operations follow the pattern:
9
+ * validate → DB operation → cache invalidate/re-cache → audit log
10
+ *
11
+ * Uses fire-and-forget audit logging — audit failures never block
12
+ * the primary operation.
13
+ *
14
+ * @see role-repository.ts — Database operations
15
+ * @see mapping-repository.ts — Role-permission join table operations
16
+ * @see cache.ts — Redis cache operations
17
+ */
18
+ import { insertRole, findRoleById as repoFindRoleById, findRoleBySlug as repoFindRoleBySlug, updateRole as repoUpdateRole, deleteRole as repoDeleteRole, listRolesByApplication as repoListRolesByApplication, roleSlugExists, countUsersWithRole, } from './role-repository.js';
19
+ import { assignPermissionsToRole as repoAssignPermissions, removePermissionsFromRole as repoRemovePermissions, getPermissionsForRole as repoGetPermissionsForRole, } from './mapping-repository.js';
20
+ import { getCachedRole, setCachedRole, invalidateRoleCache, invalidateAllUserRbacCaches, } from './cache.js';
21
+ import { generateRoleSlug, validateRoleSlug } from './slugs.js';
22
+ import { RoleNotFoundError, RbacValidationError } from './errors.js';
23
+ import { writeAuditLog } from '../lib/audit-log.js';
24
+ // ---------------------------------------------------------------------------
25
+ // Create
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Create a new role for an application.
29
+ *
30
+ * Auto-generates a slug from the name if not explicitly provided.
31
+ * Validates slug format and ensures uniqueness within the application.
32
+ *
33
+ * @param input - Role creation data
34
+ * @param actorId - Optional UUID of the admin performing the action
35
+ * @returns The newly created role
36
+ * @throws RbacValidationError if slug is invalid or already exists
37
+ */
38
+ export async function createRole(input, actorId) {
39
+ // Generate slug from name if not provided
40
+ const slug = input.slug ?? generateRoleSlug(input.name);
41
+ // Validate slug format
42
+ if (!validateRoleSlug(slug)) {
43
+ throw new RbacValidationError(`Invalid role slug format: "${slug}". Must be 1-100 chars, lowercase alphanumeric and hyphens.`);
44
+ }
45
+ // Check slug uniqueness within the application
46
+ const exists = await roleSlugExists(input.applicationId, slug);
47
+ if (exists) {
48
+ throw new RbacValidationError(`Role slug "${slug}" already exists for this application.`);
49
+ }
50
+ // Insert with the validated slug
51
+ const role = await insertRole({ ...input, slug });
52
+ // Cache the new role
53
+ await setCachedRole(role);
54
+ // Audit log (fire-and-forget)
55
+ void writeAuditLog({
56
+ eventType: 'role.created',
57
+ eventCategory: 'admin',
58
+ actorId,
59
+ metadata: { roleId: role.id, applicationId: role.applicationId, slug: role.slug },
60
+ });
61
+ return role;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Read
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * Find a role by ID. Cache-first, falls back to DB.
68
+ *
69
+ * @param id - Role UUID
70
+ * @returns Role or null if not found
71
+ */
72
+ export async function findRoleById(id) {
73
+ // Try cache first
74
+ const cached = await getCachedRole(id);
75
+ if (cached)
76
+ return cached;
77
+ // Cache miss — query DB
78
+ const role = await repoFindRoleById(id);
79
+ if (role) {
80
+ // Cache for future lookups
81
+ await setCachedRole(role);
82
+ }
83
+ return role;
84
+ }
85
+ /**
86
+ * Find a role by application ID and slug.
87
+ *
88
+ * @param applicationId - Application UUID
89
+ * @param slug - Role slug
90
+ * @returns Role or null if not found
91
+ */
92
+ export async function findRoleBySlug(applicationId, slug) {
93
+ return repoFindRoleBySlug(applicationId, slug);
94
+ }
95
+ /**
96
+ * List all roles for an application.
97
+ *
98
+ * @param applicationId - Application UUID
99
+ * @returns Array of roles
100
+ */
101
+ export async function listRolesByApplication(applicationId) {
102
+ return repoListRolesByApplication(applicationId);
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Update
106
+ // ---------------------------------------------------------------------------
107
+ /**
108
+ * Update a role by ID.
109
+ *
110
+ * If the slug is being changed, validates the new slug format and
111
+ * checks uniqueness within the application. Invalidates and re-caches
112
+ * the role after update.
113
+ *
114
+ * @param id - Role UUID
115
+ * @param input - Fields to update
116
+ * @param actorId - Optional UUID of the admin performing the action
117
+ * @returns Updated role
118
+ * @throws RoleNotFoundError if role doesn't exist
119
+ * @throws RbacValidationError if new slug is invalid or already exists
120
+ */
121
+ export async function updateRole(id, input, actorId) {
122
+ // Verify role exists (needed for applicationId if slug is changing)
123
+ const existing = await repoFindRoleById(id);
124
+ if (!existing) {
125
+ throw new RoleNotFoundError(id);
126
+ }
127
+ // If slug is changing, validate format and uniqueness
128
+ if (input.slug !== undefined && input.slug !== existing.slug) {
129
+ if (!validateRoleSlug(input.slug)) {
130
+ throw new RbacValidationError(`Invalid role slug format: "${input.slug}". Must be 1-100 chars, lowercase alphanumeric and hyphens.`);
131
+ }
132
+ const slugTaken = await roleSlugExists(existing.applicationId, input.slug, id);
133
+ if (slugTaken) {
134
+ throw new RbacValidationError(`Role slug "${input.slug}" already exists for this application.`);
135
+ }
136
+ }
137
+ // Perform the update
138
+ const updated = await repoUpdateRole(id, input);
139
+ // Invalidate old cache and store updated role
140
+ await invalidateRoleCache(id);
141
+ await setCachedRole(updated);
142
+ // Audit log (fire-and-forget)
143
+ void writeAuditLog({
144
+ eventType: 'role.updated',
145
+ eventCategory: 'admin',
146
+ actorId,
147
+ metadata: { roleId: id, changes: input },
148
+ });
149
+ return updated;
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Delete
153
+ // ---------------------------------------------------------------------------
154
+ /**
155
+ * Delete a role by ID.
156
+ *
157
+ * By default (force=false), refuses to delete roles that have users
158
+ * assigned. Pass force=true to delete regardless — CASCADE will
159
+ * remove user_roles and role_permissions entries.
160
+ *
161
+ * @param id - Role UUID
162
+ * @param force - If true, delete even if users are assigned
163
+ * @param actorId - Optional UUID of the admin performing the action
164
+ * @throws RoleNotFoundError if role doesn't exist
165
+ * @throws RbacValidationError if users are assigned and force is false
166
+ */
167
+ export async function deleteRole(id, force = false, actorId) {
168
+ // Verify role exists
169
+ const existing = await repoFindRoleById(id);
170
+ if (!existing) {
171
+ throw new RoleNotFoundError(id);
172
+ }
173
+ // Deletion guard: check for assigned users
174
+ if (!force) {
175
+ const userCount = await countUsersWithRole(id);
176
+ if (userCount > 0) {
177
+ throw new RbacValidationError(`Cannot delete role "${existing.slug}": ${userCount} user(s) still assigned. Use force=true to override.`);
178
+ }
179
+ }
180
+ // Delete the role (CASCADE handles related records)
181
+ await repoDeleteRole(id);
182
+ // Invalidate caches
183
+ await invalidateRoleCache(id);
184
+ // User caches may be stale if role had users (force=true case)
185
+ if (force) {
186
+ await invalidateAllUserRbacCaches();
187
+ }
188
+ // Audit log (fire-and-forget)
189
+ void writeAuditLog({
190
+ eventType: 'role.deleted',
191
+ eventCategory: 'admin',
192
+ actorId,
193
+ metadata: { roleId: id, slug: existing.slug, force },
194
+ });
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // Role-Permission management
198
+ // ---------------------------------------------------------------------------
199
+ /**
200
+ * Assign permissions to a role.
201
+ *
202
+ * Delegates to the mapping repository for bulk insert. Invalidates
203
+ * all user RBAC caches since user permissions may have changed.
204
+ *
205
+ * @param roleId - Role UUID
206
+ * @param permissionIds - Array of permission UUIDs to assign
207
+ * @param actorId - Optional UUID of the admin performing the action
208
+ */
209
+ export async function assignPermissionsToRole(roleId, permissionIds, actorId) {
210
+ if (permissionIds.length === 0)
211
+ return;
212
+ await repoAssignPermissions(roleId, permissionIds);
213
+ // Invalidate all user RBAC caches — permissions through roles may have changed
214
+ await invalidateAllUserRbacCaches();
215
+ // Audit log (fire-and-forget)
216
+ void writeAuditLog({
217
+ eventType: 'role.permissions.assigned',
218
+ eventCategory: 'admin',
219
+ actorId,
220
+ metadata: { roleId, permissionIds },
221
+ });
222
+ }
223
+ /**
224
+ * Remove permissions from a role.
225
+ *
226
+ * Delegates to the mapping repository. Invalidates all user RBAC
227
+ * caches since user permissions may have changed.
228
+ *
229
+ * @param roleId - Role UUID
230
+ * @param permissionIds - Array of permission UUIDs to remove
231
+ * @param actorId - Optional UUID of the admin performing the action
232
+ */
233
+ export async function removePermissionsFromRole(roleId, permissionIds, actorId) {
234
+ if (permissionIds.length === 0)
235
+ return;
236
+ await repoRemovePermissions(roleId, permissionIds);
237
+ // Invalidate all user RBAC caches
238
+ await invalidateAllUserRbacCaches();
239
+ // Audit log (fire-and-forget)
240
+ void writeAuditLog({
241
+ eventType: 'role.permissions.removed',
242
+ eventCategory: 'admin',
243
+ actorId,
244
+ metadata: { roleId, permissionIds },
245
+ });
246
+ }
247
+ /**
248
+ * Get all permissions assigned to a role.
249
+ *
250
+ * @param roleId - Role UUID
251
+ * @returns Array of permissions
252
+ */
253
+ export async function getPermissionsForRole(roleId) {
254
+ return repoGetPermissionsForRole(roleId);
255
+ }
256
+ //# sourceMappingURL=role-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-service.js","sourceRoot":"","sources":["../../src/rbac/role-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Slug generation and validation utilities for RBAC.
3
+ *
4
+ * Two types of slugs are used in the RBAC module:
5
+ *
6
+ * 1. **Role slugs** — kebab-case identifiers (e.g., "crm-editor").
7
+ * Same format as organization/application slugs. Auto-generated
8
+ * from role names or provided manually.
9
+ *
10
+ * 2. **Permission slugs** — colon-separated namespaced identifiers
11
+ * following the `module:resource:action` format (e.g., "crm:contacts:read").
12
+ * Each segment must be lowercase alphanumeric with hyphens.
13
+ * Minimum 3 segments required.
14
+ *
15
+ * This module provides:
16
+ * - `generateRoleSlug(name)` — Derive a role slug from a role name
17
+ * - `validateRoleSlug(slug)` — Check role slug format (kebab-case, 1-100 chars)
18
+ * - `validatePermissionSlug(slug)` — Check permission slug follows module:resource:action
19
+ * - `parsePermissionSlug(slug)` — Decompose a permission slug into its parts
20
+ */
21
+ /**
22
+ * Generate a URL-safe slug from a role name.
23
+ *
24
+ * Uses the same slugification logic as organizations/applications:
25
+ * 1. Convert to lowercase
26
+ * 2. Replace non-alphanumeric characters with hyphens
27
+ * 3. Collapse multiple consecutive hyphens into one
28
+ * 4. Trim leading and trailing hyphens
29
+ * 5. Truncate to 100 characters (trimming trailing hyphens after truncation)
30
+ *
31
+ * Returns an empty string for empty/whitespace-only input — the caller
32
+ * (service layer) is responsible for validation.
33
+ *
34
+ * @param name - Role name to derive a slug from
35
+ * @returns Generated slug string
36
+ *
37
+ * @example
38
+ * generateRoleSlug('CRM Editor') // "crm-editor"
39
+ * generateRoleSlug('Invoice Approver') // "invoice-approver"
40
+ * generateRoleSlug('') // ""
41
+ */
42
+ export declare function generateRoleSlug(name: string): string;
43
+ /**
44
+ * Validate a role slug format.
45
+ *
46
+ * Rules:
47
+ * - 1–100 characters long
48
+ * - Lowercase alphanumeric characters and hyphens only
49
+ * - Cannot start or end with a hyphen
50
+ * - Empty strings are invalid
51
+ *
52
+ * Does NOT check uniqueness — that's the repository/service layer's job.
53
+ *
54
+ * @param slug - Role slug string to validate
55
+ * @returns true if the slug format is valid, false otherwise
56
+ *
57
+ * @example
58
+ * validateRoleSlug('crm-editor') // true
59
+ * validateRoleSlug('admin') // true
60
+ * validateRoleSlug('CRM Editor') // false (spaces, uppercase)
61
+ * validateRoleSlug('') // false (empty)
62
+ */
63
+ export declare function validateRoleSlug(slug: string): boolean;
64
+ /**
65
+ * Validate a permission slug follows the module:resource:action format.
66
+ *
67
+ * Rules:
68
+ * - Must have at least 3 segments separated by colons
69
+ * - Each segment must be lowercase alphanumeric with hyphens
70
+ * - Each segment must start and end with an alphanumeric character
71
+ * - Empty segments are not allowed
72
+ * - Total length must not exceed 150 characters (matches DB column)
73
+ *
74
+ * @param slug - Permission slug to validate
75
+ * @returns true if the slug format is valid, false otherwise
76
+ *
77
+ * @example
78
+ * validatePermissionSlug('crm:contacts:read') // true
79
+ * validatePermissionSlug('admin:system:manage') // true
80
+ * validatePermissionSlug('crm:sub-module:items:write') // true (4+ segments OK)
81
+ * validatePermissionSlug('contacts-read') // false (no colons)
82
+ * validatePermissionSlug('a:b') // false (only 2 segments)
83
+ * validatePermissionSlug('') // false (empty)
84
+ */
85
+ export declare function validatePermissionSlug(slug: string): boolean;
86
+ /** Parsed components of a permission slug. */
87
+ export interface ParsedPermissionSlug {
88
+ module: string;
89
+ resource: string;
90
+ action: string;
91
+ }
92
+ /**
93
+ * Parse a permission slug into its module, resource, and action components.
94
+ *
95
+ * The first segment is the module, the last segment is the action,
96
+ * and everything in between is joined as the resource. This supports
97
+ * both 3-segment slugs (crm:contacts:read) and 4+ segment slugs
98
+ * (crm:sub-module:items:write → module="crm", resource="sub-module:items", action="write").
99
+ *
100
+ * Returns null if the slug is invalid (fails validatePermissionSlug).
101
+ *
102
+ * @param slug - Permission slug to parse
103
+ * @returns Parsed components or null if invalid
104
+ *
105
+ * @example
106
+ * parsePermissionSlug('crm:contacts:read')
107
+ * // { module: 'crm', resource: 'contacts', action: 'read' }
108
+ *
109
+ * parsePermissionSlug('crm:sub:items:write')
110
+ * // { module: 'crm', resource: 'sub:items', action: 'write' }
111
+ *
112
+ * parsePermissionSlug('invalid')
113
+ * // null
114
+ */
115
+ export declare function parsePermissionSlug(slug: string): ParsedPermissionSlug | null;
116
+ //# sourceMappingURL=slugs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugs.d.ts","sourceRoot":"","sources":["../../src/rbac/slugs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA2BH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOtD;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgB5D;AAMD,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAY7E"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Slug generation and validation utilities for RBAC.
3
+ *
4
+ * Two types of slugs are used in the RBAC module:
5
+ *
6
+ * 1. **Role slugs** — kebab-case identifiers (e.g., "crm-editor").
7
+ * Same format as organization/application slugs. Auto-generated
8
+ * from role names or provided manually.
9
+ *
10
+ * 2. **Permission slugs** — colon-separated namespaced identifiers
11
+ * following the `module:resource:action` format (e.g., "crm:contacts:read").
12
+ * Each segment must be lowercase alphanumeric with hyphens.
13
+ * Minimum 3 segments required.
14
+ *
15
+ * This module provides:
16
+ * - `generateRoleSlug(name)` — Derive a role slug from a role name
17
+ * - `validateRoleSlug(slug)` — Check role slug format (kebab-case, 1-100 chars)
18
+ * - `validatePermissionSlug(slug)` — Check permission slug follows module:resource:action
19
+ * - `parsePermissionSlug(slug)` — Decompose a permission slug into its parts
20
+ */
21
+ // ---------------------------------------------------------------------------
22
+ // Role slug format
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Role slug format regex: 1–100 characters, lowercase alphanumeric + hyphens.
26
+ * Must start and end with an alphanumeric character (no leading/trailing hyphens).
27
+ * Single character slugs are allowed (unlike organization slugs which require 3+).
28
+ */
29
+ const ROLE_SLUG_REGEX = /^[a-z0-9]([a-z0-9-]{0,98}[a-z0-9])?$/;
30
+ // ---------------------------------------------------------------------------
31
+ // Permission slug format
32
+ // ---------------------------------------------------------------------------
33
+ /**
34
+ * Each segment of a permission slug: lowercase alphanumeric + hyphens,
35
+ * must start and end with an alphanumeric character.
36
+ */
37
+ const PERMISSION_SEGMENT_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
38
+ // ---------------------------------------------------------------------------
39
+ // Role slug generation
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Generate a URL-safe slug from a role name.
43
+ *
44
+ * Uses the same slugification logic as organizations/applications:
45
+ * 1. Convert to lowercase
46
+ * 2. Replace non-alphanumeric characters with hyphens
47
+ * 3. Collapse multiple consecutive hyphens into one
48
+ * 4. Trim leading and trailing hyphens
49
+ * 5. Truncate to 100 characters (trimming trailing hyphens after truncation)
50
+ *
51
+ * Returns an empty string for empty/whitespace-only input — the caller
52
+ * (service layer) is responsible for validation.
53
+ *
54
+ * @param name - Role name to derive a slug from
55
+ * @returns Generated slug string
56
+ *
57
+ * @example
58
+ * generateRoleSlug('CRM Editor') // "crm-editor"
59
+ * generateRoleSlug('Invoice Approver') // "invoice-approver"
60
+ * generateRoleSlug('') // ""
61
+ */
62
+ export function generateRoleSlug(name) {
63
+ if (!name || !name.trim()) {
64
+ return '';
65
+ }
66
+ const slug = name
67
+ .toLowerCase()
68
+ // Replace any character that's not a-z, 0-9, or hyphen with a hyphen
69
+ .replace(/[^a-z0-9-]/g, '-')
70
+ // Collapse consecutive hyphens into a single hyphen
71
+ .replace(/-+/g, '-')
72
+ // Remove leading and trailing hyphens
73
+ .replace(/^-+|-+$/g, '');
74
+ // Truncate to 100 characters, then trim any trailing hyphen caused by truncation
75
+ return slug.slice(0, 100).replace(/-+$/, '');
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // Role slug validation
79
+ // ---------------------------------------------------------------------------
80
+ /**
81
+ * Validate a role slug format.
82
+ *
83
+ * Rules:
84
+ * - 1–100 characters long
85
+ * - Lowercase alphanumeric characters and hyphens only
86
+ * - Cannot start or end with a hyphen
87
+ * - Empty strings are invalid
88
+ *
89
+ * Does NOT check uniqueness — that's the repository/service layer's job.
90
+ *
91
+ * @param slug - Role slug string to validate
92
+ * @returns true if the slug format is valid, false otherwise
93
+ *
94
+ * @example
95
+ * validateRoleSlug('crm-editor') // true
96
+ * validateRoleSlug('admin') // true
97
+ * validateRoleSlug('CRM Editor') // false (spaces, uppercase)
98
+ * validateRoleSlug('') // false (empty)
99
+ */
100
+ export function validateRoleSlug(slug) {
101
+ // Must be 1–100 characters
102
+ if (!slug || slug.length > 100) {
103
+ return false;
104
+ }
105
+ return ROLE_SLUG_REGEX.test(slug);
106
+ }
107
+ // ---------------------------------------------------------------------------
108
+ // Permission slug validation
109
+ // ---------------------------------------------------------------------------
110
+ /**
111
+ * Validate a permission slug follows the module:resource:action format.
112
+ *
113
+ * Rules:
114
+ * - Must have at least 3 segments separated by colons
115
+ * - Each segment must be lowercase alphanumeric with hyphens
116
+ * - Each segment must start and end with an alphanumeric character
117
+ * - Empty segments are not allowed
118
+ * - Total length must not exceed 150 characters (matches DB column)
119
+ *
120
+ * @param slug - Permission slug to validate
121
+ * @returns true if the slug format is valid, false otherwise
122
+ *
123
+ * @example
124
+ * validatePermissionSlug('crm:contacts:read') // true
125
+ * validatePermissionSlug('admin:system:manage') // true
126
+ * validatePermissionSlug('crm:sub-module:items:write') // true (4+ segments OK)
127
+ * validatePermissionSlug('contacts-read') // false (no colons)
128
+ * validatePermissionSlug('a:b') // false (only 2 segments)
129
+ * validatePermissionSlug('') // false (empty)
130
+ */
131
+ export function validatePermissionSlug(slug) {
132
+ // Must be non-empty and within DB column limit (VARCHAR(150))
133
+ if (!slug || slug.length > 150) {
134
+ return false;
135
+ }
136
+ // Split on colons — need at least 3 segments
137
+ const segments = slug.split(':');
138
+ if (segments.length < 3) {
139
+ return false;
140
+ }
141
+ // Each segment must match the segment format
142
+ return segments.every((segment) => segment.length > 0 && PERMISSION_SEGMENT_REGEX.test(segment));
143
+ }
144
+ /**
145
+ * Parse a permission slug into its module, resource, and action components.
146
+ *
147
+ * The first segment is the module, the last segment is the action,
148
+ * and everything in between is joined as the resource. This supports
149
+ * both 3-segment slugs (crm:contacts:read) and 4+ segment slugs
150
+ * (crm:sub-module:items:write → module="crm", resource="sub-module:items", action="write").
151
+ *
152
+ * Returns null if the slug is invalid (fails validatePermissionSlug).
153
+ *
154
+ * @param slug - Permission slug to parse
155
+ * @returns Parsed components or null if invalid
156
+ *
157
+ * @example
158
+ * parsePermissionSlug('crm:contacts:read')
159
+ * // { module: 'crm', resource: 'contacts', action: 'read' }
160
+ *
161
+ * parsePermissionSlug('crm:sub:items:write')
162
+ * // { module: 'crm', resource: 'sub:items', action: 'write' }
163
+ *
164
+ * parsePermissionSlug('invalid')
165
+ * // null
166
+ */
167
+ export function parsePermissionSlug(slug) {
168
+ if (!validatePermissionSlug(slug)) {
169
+ return null;
170
+ }
171
+ const segments = slug.split(':');
172
+ // First segment = module, last = action, middle = resource
173
+ const module = segments[0];
174
+ const action = segments[segments.length - 1];
175
+ const resource = segments.slice(1, -1).join(':');
176
+ return { module, resource, action };
177
+ }
178
+ //# sourceMappingURL=slugs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slugs.js","sourceRoot":"","sources":["../../src/rbac/slugs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,eAAe,GAAG,sCAAsC,CAAC;AAE/D,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,wBAAwB,GAAG,iCAAiC,CAAC;AAEnE,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI;SACd,WAAW,EAAE;QACd,qEAAqE;SACpE,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;QAC5B,oDAAoD;SACnD,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;QACpB,sCAAsC;SACrC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE3B,iFAAiF;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,2BAA2B;IAC3B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,8DAA8D;IAC9D,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAC7C,OAAO,QAAQ,CAAC,KAAK,CACnB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAC1E,CAAC;AACJ,CAAC;AAaD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,2DAA2D;IAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEjD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACtC,CAAC"}