@portaidentity/cli 1.7.3 → 1.9.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 (328) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +125 -105
  3. package/dist/admin/admin-date-time.d.ts +6 -0
  4. package/dist/admin/admin-date-time.d.ts.map +1 -0
  5. package/dist/admin/admin-date-time.js +33 -0
  6. package/dist/admin/admin-date-time.js.map +1 -0
  7. package/dist/admin/application-client-features.d.ts +38 -0
  8. package/dist/admin/application-client-features.d.ts.map +1 -0
  9. package/dist/admin/application-client-features.js +545 -0
  10. package/dist/admin/application-client-features.js.map +1 -0
  11. package/dist/admin/application-controller.d.ts +58 -0
  12. package/dist/admin/application-controller.d.ts.map +1 -0
  13. package/dist/admin/application-controller.js +286 -0
  14. package/dist/admin/application-controller.js.map +1 -0
  15. package/dist/admin/application-dialogs.d.ts +91 -0
  16. package/dist/admin/application-dialogs.d.ts.map +1 -0
  17. package/dist/admin/application-dialogs.js +250 -0
  18. package/dist/admin/application-dialogs.js.map +1 -0
  19. package/dist/admin/application-rbac-controller.d.ts +56 -0
  20. package/dist/admin/application-rbac-controller.d.ts.map +1 -0
  21. package/dist/admin/application-rbac-controller.js +280 -0
  22. package/dist/admin/application-rbac-controller.js.map +1 -0
  23. package/dist/admin/application-rbac-features.d.ts +51 -0
  24. package/dist/admin/application-rbac-features.d.ts.map +1 -0
  25. package/dist/admin/application-rbac-features.js +175 -0
  26. package/dist/admin/application-rbac-features.js.map +1 -0
  27. package/dist/admin/application-rbac-workspace.d.ts +62 -0
  28. package/dist/admin/application-rbac-workspace.d.ts.map +1 -0
  29. package/dist/admin/application-rbac-workspace.js +344 -0
  30. package/dist/admin/application-rbac-workspace.js.map +1 -0
  31. package/dist/admin/application-runtime.d.ts +44 -0
  32. package/dist/admin/application-runtime.d.ts.map +1 -0
  33. package/dist/admin/application-runtime.js +183 -0
  34. package/dist/admin/application-runtime.js.map +1 -0
  35. package/dist/admin/application-service.d.ts +39 -0
  36. package/dist/admin/application-service.d.ts.map +1 -0
  37. package/dist/admin/application-service.js +248 -0
  38. package/dist/admin/application-service.js.map +1 -0
  39. package/dist/admin/application-state.d.ts +109 -0
  40. package/dist/admin/application-state.d.ts.map +1 -0
  41. package/dist/admin/application-state.js +3 -0
  42. package/dist/admin/application-state.js.map +1 -0
  43. package/dist/admin/application-workspace.d.ts +77 -0
  44. package/dist/admin/application-workspace.d.ts.map +1 -0
  45. package/dist/admin/application-workspace.js +302 -0
  46. package/dist/admin/application-workspace.js.map +1 -0
  47. package/dist/admin/application.d.ts +88 -0
  48. package/dist/admin/application.d.ts.map +1 -0
  49. package/dist/admin/application.js +932 -0
  50. package/dist/admin/application.js.map +1 -0
  51. package/dist/admin/client-authentication-dialog.d.ts +77 -0
  52. package/dist/admin/client-authentication-dialog.d.ts.map +1 -0
  53. package/dist/admin/client-authentication-dialog.js +217 -0
  54. package/dist/admin/client-authentication-dialog.js.map +1 -0
  55. package/dist/admin/client-controller.d.ts +58 -0
  56. package/dist/admin/client-controller.d.ts.map +1 -0
  57. package/dist/admin/client-controller.js +462 -0
  58. package/dist/admin/client-controller.js.map +1 -0
  59. package/dist/admin/client-credential-dialogs.d.ts +36 -0
  60. package/dist/admin/client-credential-dialogs.d.ts.map +1 -0
  61. package/dist/admin/client-credential-dialogs.js +145 -0
  62. package/dist/admin/client-credential-dialogs.js.map +1 -0
  63. package/dist/admin/client-dialogs.d.ts +64 -0
  64. package/dist/admin/client-dialogs.d.ts.map +1 -0
  65. package/dist/admin/client-dialogs.js +110 -0
  66. package/dist/admin/client-dialogs.js.map +1 -0
  67. package/dist/admin/client-form-scroller.d.ts +17 -0
  68. package/dist/admin/client-form-scroller.d.ts.map +1 -0
  69. package/dist/admin/client-form-scroller.js +67 -0
  70. package/dist/admin/client-form-scroller.js.map +1 -0
  71. package/dist/admin/client-registration-dialog.d.ts +27 -0
  72. package/dist/admin/client-registration-dialog.d.ts.map +1 -0
  73. package/dist/admin/client-registration-dialog.js +195 -0
  74. package/dist/admin/client-registration-dialog.js.map +1 -0
  75. package/dist/admin/client-service.d.ts +41 -0
  76. package/dist/admin/client-service.d.ts.map +1 -0
  77. package/dist/admin/client-service.js +486 -0
  78. package/dist/admin/client-service.js.map +1 -0
  79. package/dist/admin/client-state.d.ts +159 -0
  80. package/dist/admin/client-state.d.ts.map +1 -0
  81. package/dist/admin/client-state.js +3 -0
  82. package/dist/admin/client-state.js.map +1 -0
  83. package/dist/admin/client-workspace.d.ts +88 -0
  84. package/dist/admin/client-workspace.d.ts.map +1 -0
  85. package/dist/admin/client-workspace.js +491 -0
  86. package/dist/admin/client-workspace.js.map +1 -0
  87. package/dist/admin/delete-confirmation-layout.d.ts +38 -0
  88. package/dist/admin/delete-confirmation-layout.d.ts.map +1 -0
  89. package/dist/admin/delete-confirmation-layout.js +52 -0
  90. package/dist/admin/delete-confirmation-layout.js.map +1 -0
  91. package/dist/admin/index.d.ts +64 -0
  92. package/dist/admin/index.d.ts.map +1 -0
  93. package/dist/admin/index.js +31 -0
  94. package/dist/admin/index.js.map +1 -0
  95. package/dist/admin/organization-controller.d.ts +77 -0
  96. package/dist/admin/organization-controller.d.ts.map +1 -0
  97. package/dist/admin/organization-controller.js +472 -0
  98. package/dist/admin/organization-controller.js.map +1 -0
  99. package/dist/admin/organization-dialogs.d.ts +63 -0
  100. package/dist/admin/organization-dialogs.d.ts.map +1 -0
  101. package/dist/admin/organization-dialogs.js +303 -0
  102. package/dist/admin/organization-dialogs.js.map +1 -0
  103. package/dist/admin/organization-service.d.ts +110 -0
  104. package/dist/admin/organization-service.d.ts.map +1 -0
  105. package/dist/admin/organization-service.js +548 -0
  106. package/dist/admin/organization-service.js.map +1 -0
  107. package/dist/admin/organization-workspace.d.ts +29 -0
  108. package/dist/admin/organization-workspace.d.ts.map +1 -0
  109. package/dist/admin/organization-workspace.js +421 -0
  110. package/dist/admin/organization-workspace.js.map +1 -0
  111. package/dist/admin/portability-controller.d.ts +83 -0
  112. package/dist/admin/portability-controller.d.ts.map +1 -0
  113. package/dist/admin/portability-controller.js +466 -0
  114. package/dist/admin/portability-controller.js.map +1 -0
  115. package/dist/admin/portability-service.d.ts +31 -0
  116. package/dist/admin/portability-service.d.ts.map +1 -0
  117. package/dist/admin/portability-service.js +51 -0
  118. package/dist/admin/portability-service.js.map +1 -0
  119. package/dist/admin/portability-state.d.ts +88 -0
  120. package/dist/admin/portability-state.d.ts.map +1 -0
  121. package/dist/admin/portability-state.js +3 -0
  122. package/dist/admin/portability-state.js.map +1 -0
  123. package/dist/admin/portability-workspace.d.ts +78 -0
  124. package/dist/admin/portability-workspace.d.ts.map +1 -0
  125. package/dist/admin/portability-workspace.js +337 -0
  126. package/dist/admin/portability-workspace.js.map +1 -0
  127. package/dist/admin/presentation.d.ts +46 -0
  128. package/dist/admin/presentation.d.ts.map +1 -0
  129. package/dist/admin/presentation.js +271 -0
  130. package/dist/admin/presentation.js.map +1 -0
  131. package/dist/admin/rbac-dialogs.d.ts +73 -0
  132. package/dist/admin/rbac-dialogs.d.ts.map +1 -0
  133. package/dist/admin/rbac-dialogs.js +345 -0
  134. package/dist/admin/rbac-dialogs.js.map +1 -0
  135. package/dist/admin/rbac-service.d.ts +47 -0
  136. package/dist/admin/rbac-service.d.ts.map +1 -0
  137. package/dist/admin/rbac-service.js +258 -0
  138. package/dist/admin/rbac-service.js.map +1 -0
  139. package/dist/admin/rbac-state.d.ts +129 -0
  140. package/dist/admin/rbac-state.d.ts.map +1 -0
  141. package/dist/admin/rbac-state.js +136 -0
  142. package/dist/admin/rbac-state.js.map +1 -0
  143. package/dist/admin/selectable-read-only-input.d.ts +18 -0
  144. package/dist/admin/selectable-read-only-input.d.ts.map +1 -0
  145. package/dist/admin/selectable-read-only-input.js +24 -0
  146. package/dist/admin/selectable-read-only-input.js.map +1 -0
  147. package/dist/admin/session-service.d.ts +169 -0
  148. package/dist/admin/session-service.d.ts.map +1 -0
  149. package/dist/admin/session-service.js +353 -0
  150. package/dist/admin/session-service.js.map +1 -0
  151. package/dist/admin/state.d.ts +301 -0
  152. package/dist/admin/state.d.ts.map +1 -0
  153. package/dist/admin/state.js +10 -0
  154. package/dist/admin/state.js.map +1 -0
  155. package/dist/admin/system-config-controller.d.ts +58 -0
  156. package/dist/admin/system-config-controller.d.ts.map +1 -0
  157. package/dist/admin/system-config-controller.js +218 -0
  158. package/dist/admin/system-config-controller.js.map +1 -0
  159. package/dist/admin/system-config-service.d.ts +54 -0
  160. package/dist/admin/system-config-service.d.ts.map +1 -0
  161. package/dist/admin/system-config-service.js +180 -0
  162. package/dist/admin/system-config-service.js.map +1 -0
  163. package/dist/admin/system-config-state.d.ts +77 -0
  164. package/dist/admin/system-config-state.d.ts.map +1 -0
  165. package/dist/admin/system-config-state.js +78 -0
  166. package/dist/admin/system-config-state.js.map +1 -0
  167. package/dist/admin/system-config-workspace.d.ts +47 -0
  168. package/dist/admin/system-config-workspace.d.ts.map +1 -0
  169. package/dist/admin/system-config-workspace.js +217 -0
  170. package/dist/admin/system-config-workspace.js.map +1 -0
  171. package/dist/admin/user-controller-types.d.ts +67 -0
  172. package/dist/admin/user-controller-types.d.ts.map +1 -0
  173. package/dist/admin/user-controller-types.js +3 -0
  174. package/dist/admin/user-controller-types.js.map +1 -0
  175. package/dist/admin/user-controller.d.ts +6 -0
  176. package/dist/admin/user-controller.d.ts.map +1 -0
  177. package/dist/admin/user-controller.js +674 -0
  178. package/dist/admin/user-controller.js.map +1 -0
  179. package/dist/admin/user-dialog-fields.d.ts +44 -0
  180. package/dist/admin/user-dialog-fields.d.ts.map +1 -0
  181. package/dist/admin/user-dialog-fields.js +130 -0
  182. package/dist/admin/user-dialog-fields.js.map +1 -0
  183. package/dist/admin/user-dialogs.d.ts +66 -0
  184. package/dist/admin/user-dialogs.d.ts.map +1 -0
  185. package/dist/admin/user-dialogs.js +374 -0
  186. package/dist/admin/user-dialogs.js.map +1 -0
  187. package/dist/admin/user-role-controller.d.ts +82 -0
  188. package/dist/admin/user-role-controller.d.ts.map +1 -0
  189. package/dist/admin/user-role-controller.js +337 -0
  190. package/dist/admin/user-role-controller.js.map +1 -0
  191. package/dist/admin/user-role-dialog.d.ts +94 -0
  192. package/dist/admin/user-role-dialog.d.ts.map +1 -0
  193. package/dist/admin/user-role-dialog.js +265 -0
  194. package/dist/admin/user-role-dialog.js.map +1 -0
  195. package/dist/admin/user-service-types.d.ts +100 -0
  196. package/dist/admin/user-service-types.d.ts.map +1 -0
  197. package/dist/admin/user-service-types.js +3 -0
  198. package/dist/admin/user-service-types.js.map +1 -0
  199. package/dist/admin/user-service.d.ts +14 -0
  200. package/dist/admin/user-service.d.ts.map +1 -0
  201. package/dist/admin/user-service.js +612 -0
  202. package/dist/admin/user-service.js.map +1 -0
  203. package/dist/admin/user-state.d.ts +158 -0
  204. package/dist/admin/user-state.d.ts.map +1 -0
  205. package/dist/admin/user-state.js +3 -0
  206. package/dist/admin/user-state.js.map +1 -0
  207. package/dist/admin/user-workspace.d.ts +65 -0
  208. package/dist/admin/user-workspace.d.ts.map +1 -0
  209. package/dist/admin/user-workspace.js +366 -0
  210. package/dist/admin/user-workspace.js.map +1 -0
  211. package/dist/auth/browser-flow.d.ts +27 -1
  212. package/dist/auth/browser-flow.d.ts.map +1 -1
  213. package/dist/auth/browser-flow.js +55 -33
  214. package/dist/auth/browser-flow.js.map +1 -1
  215. package/dist/auth/callback-server.d.ts.map +1 -1
  216. package/dist/auth/callback-server.js +5 -9
  217. package/dist/auth/callback-server.js.map +1 -1
  218. package/dist/auth/id-token-verifier.d.ts +39 -0
  219. package/dist/auth/id-token-verifier.d.ts.map +1 -0
  220. package/dist/auth/id-token-verifier.js +104 -0
  221. package/dist/auth/id-token-verifier.js.map +1 -0
  222. package/dist/auth/login-coordinator.d.ts +72 -0
  223. package/dist/auth/login-coordinator.d.ts.map +1 -0
  224. package/dist/auth/login-coordinator.js +190 -0
  225. package/dist/auth/login-coordinator.js.map +1 -0
  226. package/dist/auth/metadata.d.ts +2 -1
  227. package/dist/auth/metadata.d.ts.map +1 -1
  228. package/dist/auth/metadata.js +67 -6
  229. package/dist/auth/metadata.js.map +1 -1
  230. package/dist/auth/types.d.ts +14 -0
  231. package/dist/auth/types.d.ts.map +1 -1
  232. package/dist/client-factory.d.ts.map +1 -1
  233. package/dist/client-factory.js +15 -2
  234. package/dist/client-factory.js.map +1 -1
  235. package/dist/commands/admin.d.ts +22 -0
  236. package/dist/commands/admin.d.ts.map +1 -0
  237. package/dist/commands/admin.js +112 -0
  238. package/dist/commands/admin.js.map +1 -0
  239. package/dist/commands/app-claim.d.ts.map +1 -1
  240. package/dist/commands/app-claim.js +11 -4
  241. package/dist/commands/app-claim.js.map +1 -1
  242. package/dist/commands/app-module.d.ts.map +1 -1
  243. package/dist/commands/app-module.js +61 -12
  244. package/dist/commands/app-module.js.map +1 -1
  245. package/dist/commands/app-permission.d.ts.map +1 -1
  246. package/dist/commands/app-permission.js +66 -24
  247. package/dist/commands/app-permission.js.map +1 -1
  248. package/dist/commands/app-role.d.ts.map +1 -1
  249. package/dist/commands/app-role.js +48 -27
  250. package/dist/commands/app-role.js.map +1 -1
  251. package/dist/commands/app.d.ts.map +1 -1
  252. package/dist/commands/app.js +37 -25
  253. package/dist/commands/app.js.map +1 -1
  254. package/dist/commands/bulk.d.ts.map +1 -1
  255. package/dist/commands/bulk.js +9 -2
  256. package/dist/commands/bulk.js.map +1 -1
  257. package/dist/commands/client-secret.d.ts.map +1 -1
  258. package/dist/commands/client-secret.js +12 -6
  259. package/dist/commands/client-secret.js.map +1 -1
  260. package/dist/commands/client.d.ts +3 -2
  261. package/dist/commands/client.d.ts.map +1 -1
  262. package/dist/commands/client.js +102 -43
  263. package/dist/commands/client.js.map +1 -1
  264. package/dist/commands/config.d.ts +4 -0
  265. package/dist/commands/config.d.ts.map +1 -1
  266. package/dist/commands/config.js +68 -7
  267. package/dist/commands/config.js.map +1 -1
  268. package/dist/commands/export.d.ts +6 -0
  269. package/dist/commands/export.d.ts.map +1 -0
  270. package/dist/commands/export.js +182 -0
  271. package/dist/commands/export.js.map +1 -0
  272. package/dist/commands/import.d.ts +6 -0
  273. package/dist/commands/import.d.ts.map +1 -0
  274. package/dist/commands/import.js +142 -0
  275. package/dist/commands/import.js.map +1 -0
  276. package/dist/commands/keys.d.ts.map +1 -1
  277. package/dist/commands/keys.js +11 -4
  278. package/dist/commands/keys.js.map +1 -1
  279. package/dist/commands/login.d.ts.map +1 -1
  280. package/dist/commands/login.js +24 -10
  281. package/dist/commands/login.js.map +1 -1
  282. package/dist/commands/org.d.ts +2 -4
  283. package/dist/commands/org.d.ts.map +1 -1
  284. package/dist/commands/org.js +16 -76
  285. package/dist/commands/org.js.map +1 -1
  286. package/dist/commands/user-role.d.ts.map +1 -1
  287. package/dist/commands/user-role.js +19 -14
  288. package/dist/commands/user-role.js.map +1 -1
  289. package/dist/commands/user.d.ts +3 -2
  290. package/dist/commands/user.d.ts.map +1 -1
  291. package/dist/commands/user.js +47 -71
  292. package/dist/commands/user.js.map +1 -1
  293. package/dist/commands/version.d.ts +1 -1
  294. package/dist/commands/version.js +1 -1
  295. package/dist/credential-lock.d.ts +20 -0
  296. package/dist/credential-lock.d.ts.map +1 -0
  297. package/dist/credential-lock.js +130 -0
  298. package/dist/credential-lock.js.map +1 -0
  299. package/dist/credential-store.d.ts +14 -0
  300. package/dist/credential-store.d.ts.map +1 -1
  301. package/dist/credential-store.js +114 -3
  302. package/dist/credential-store.js.map +1 -1
  303. package/dist/error-handler.d.ts.map +1 -1
  304. package/dist/error-handler.js +2 -0
  305. package/dist/error-handler.js.map +1 -1
  306. package/dist/global-options.d.ts +8 -0
  307. package/dist/global-options.d.ts.map +1 -1
  308. package/dist/global-options.js +19 -8
  309. package/dist/global-options.js.map +1 -1
  310. package/dist/index.js +7 -5
  311. package/dist/index.js.map +1 -1
  312. package/dist/output.d.ts +11 -0
  313. package/dist/output.d.ts.map +1 -1
  314. package/dist/output.js +25 -6
  315. package/dist/output.js.map +1 -1
  316. package/dist/prompt.d.ts +2 -2
  317. package/dist/prompt.d.ts.map +1 -1
  318. package/dist/prompt.js +27 -7
  319. package/dist/prompt.js.map +1 -1
  320. package/package.json +7 -3
  321. package/dist/commands/gui.d.ts +0 -21
  322. package/dist/commands/gui.d.ts.map +0 -1
  323. package/dist/commands/gui.js +0 -67
  324. package/dist/commands/gui.js.map +0 -1
  325. package/dist/commands/provision.d.ts +0 -47
  326. package/dist/commands/provision.d.ts.map +0 -1
  327. package/dist/commands/provision.js +0 -444
  328. package/dist/commands/provision.js.map +0 -1
@@ -0,0 +1,674 @@
1
+ /** Selected-organization workflow controller for user administration. */
2
+ import { showCreateUserDialog, showEditUserDialog, showInviteUserDialog, showDeleteUserDialog, showSetUserPasswordDialog, showUserConfirmationDialog, } from './user-dialogs.js';
3
+ import { createAdminUserWorkspace } from './user-workspace.js';
4
+ import { openAdminUserRoleWorkflow } from './user-role-controller.js';
5
+ import { ADMIN_COMMANDS } from './presentation.js';
6
+ const DEFAULT_DIALOGS = {
7
+ create: showCreateUserDialog,
8
+ invite: showInviteUserDialog,
9
+ edit: showEditUserDialog,
10
+ setPassword: showSetUserPasswordDialog,
11
+ confirm: showUserConfirmationDialog,
12
+ delete: showDeleteUserDialog,
13
+ };
14
+ /** Returns the validated projection retained beneath transient state. */
15
+ function projection(state) {
16
+ if (state.kind === 'page')
17
+ return { kind: 'page', page: state.page };
18
+ if (state.kind === 'detail')
19
+ return { ...state };
20
+ if (state.kind === 'history')
21
+ return { ...state };
22
+ if (state.kind === 'loading' || state.kind === 'failure')
23
+ return state.previous;
24
+ return undefined;
25
+ }
26
+ /** Adds a fixed mutation outcome without changing validated remote data. */
27
+ function withOutcome(previous, outcome) {
28
+ if (!previous) {
29
+ return outcome === 'outcome-unknown'
30
+ ? { kind: 'indeterminate' }
31
+ : { kind: 'failure', failure: outcome };
32
+ }
33
+ return { ...previous, outcome };
34
+ }
35
+ /** Creates the bounded selected-organization user workflow. */
36
+ export function createAdminUserController(options) {
37
+ const dialogs = { ...DEFAULT_DIALOGS, ...options.dialogs };
38
+ const makeWorkspace = options.workspaceFactory ?? createAdminUserWorkspace;
39
+ let workspace;
40
+ let userRoleWorkflow;
41
+ let contextKey;
42
+ let organizationId;
43
+ let currentSessionEpoch = 0;
44
+ let capabilitiesKey = '';
45
+ let state = { kind: 'closed' };
46
+ let query = { page: 1 };
47
+ let generation = 0;
48
+ let operationGeneration = 0;
49
+ let operation;
50
+ let operationPrevious;
51
+ let mutationDispatched = false;
52
+ let dialogBusy = false;
53
+ let visible = false;
54
+ let recoverable = true;
55
+ let userRecoveryRequired = false;
56
+ let roleRecoveryRequired = false;
57
+ let recoveryGateRequired = false;
58
+ let disposed = false;
59
+ /** Publishes the combined recovery gate without losing either independent mutation owner. */
60
+ const publishRecoveryGate = () => {
61
+ const required = userRecoveryRequired || roleRecoveryRequired;
62
+ if (recoveryGateRequired === required)
63
+ return;
64
+ recoveryGateRequired = required;
65
+ options.setRecoveryRequired?.(required);
66
+ };
67
+ /** Updates recovery owned by ordinary user mutations and reads. */
68
+ const setUserRecoveryRequired = (required) => {
69
+ userRecoveryRequired = required;
70
+ publishRecoveryGate();
71
+ };
72
+ /** Updates recovery owned only by the focused User Roles workflow. */
73
+ const setRoleRecoveryRequired = (required) => {
74
+ roleRecoveryRequired = required;
75
+ publishRecoveryGate();
76
+ };
77
+ /** Clears both recovery owners when their authenticated organization/session context ends. */
78
+ const clearRecoveryRequired = () => {
79
+ userRecoveryRequired = false;
80
+ roleRecoveryRequired = false;
81
+ publishRecoveryGate();
82
+ };
83
+ /** Publishes one state only to the current same-context workspace. */
84
+ const publish = (next) => {
85
+ if (disposed)
86
+ return;
87
+ state = next;
88
+ workspace?.setState(next);
89
+ };
90
+ /** Mounts the current workspace only while its context remains visible and recoverable. */
91
+ const syncMount = (focus = true) => {
92
+ options.mountWorkspace(visible && recoverable && workspace ? workspace.content : null);
93
+ if (focus && visible && recoverable)
94
+ workspace?.focusCurrent();
95
+ };
96
+ /** Closes the focused User Roles modal before its owner context changes. */
97
+ const closeUserRoles = () => {
98
+ const workflow = userRoleWorkflow;
99
+ if (!workflow)
100
+ return;
101
+ userRoleWorkflow = undefined;
102
+ workflow.close();
103
+ };
104
+ /** Releases current asynchronous ownership without changing presentation state. */
105
+ const releaseOperation = (controller) => {
106
+ if (operation !== controller)
107
+ return;
108
+ operation = undefined;
109
+ operationPrevious = undefined;
110
+ mutationDispatched = false;
111
+ if (dialogBusy) {
112
+ dialogBusy = false;
113
+ options.setDialogBusy(false);
114
+ }
115
+ };
116
+ /** Returns true only for the still-current asynchronous continuation. */
117
+ const owns = (contextGeneration, capturedOperation, controller) => !disposed &&
118
+ generation === contextGeneration &&
119
+ operationGeneration === capturedOperation &&
120
+ operation === controller &&
121
+ !controller.signal.aborted;
122
+ /** Enters the established application authentication flow. */
123
+ const sessionInvalid = () => {
124
+ closeUserRoles();
125
+ generation += 1;
126
+ operationGeneration += 1;
127
+ operation?.abort();
128
+ operation = undefined;
129
+ operationPrevious = undefined;
130
+ mutationDispatched = false;
131
+ if (dialogBusy) {
132
+ dialogBusy = false;
133
+ options.setDialogBusy(false);
134
+ }
135
+ visible = false;
136
+ workspace?.clear();
137
+ syncMount();
138
+ options.requestAuthentication();
139
+ };
140
+ /** Cancels only a replaceable read, leaving dialog and mutation ownership untouched. */
141
+ const cancelRead = () => {
142
+ if (!operation || dialogBusy || mutationDispatched)
143
+ return;
144
+ operationGeneration += 1;
145
+ operation.abort();
146
+ operation = undefined;
147
+ operationPrevious = undefined;
148
+ };
149
+ /** Starts a replaceable read and retains only validated prior state while it runs. */
150
+ const startRead = async (invoke, accept, reconcilesMutation = true) => {
151
+ if (!organizationId || !options.readOperations())
152
+ return;
153
+ cancelRead();
154
+ if (operation)
155
+ return;
156
+ const controller = new AbortController();
157
+ operation = controller;
158
+ mutationDispatched = false;
159
+ operationPrevious = projection(state);
160
+ const contextGeneration = generation;
161
+ const capturedOperation = ++operationGeneration;
162
+ publish(operationPrevious ? { kind: 'loading', previous: operationPrevious } : { kind: 'loading' });
163
+ try {
164
+ const result = await invoke();
165
+ if (!owns(contextGeneration, capturedOperation, controller))
166
+ return;
167
+ if (result.kind === 'session-invalid') {
168
+ sessionInvalid();
169
+ }
170
+ else if (result.kind === 'success') {
171
+ if (reconcilesMutation)
172
+ setUserRecoveryRequired(false);
173
+ publish(accept(result.value));
174
+ if (visible && recoverable)
175
+ workspace?.focusCurrent();
176
+ }
177
+ else {
178
+ publish(operationPrevious
179
+ ? { kind: 'failure', failure: result.failure, previous: operationPrevious }
180
+ : { kind: 'failure', failure: result.failure });
181
+ }
182
+ }
183
+ finally {
184
+ releaseOperation(controller);
185
+ }
186
+ };
187
+ /** Loads the current exact query from the selected organization. */
188
+ const loadPage = () => {
189
+ const selectedOrganization = organizationId;
190
+ const operations = options.readOperations();
191
+ if (!selectedOrganization || !operations)
192
+ return Promise.resolve();
193
+ return startRead(() => operations.list(selectedOrganization, { ...query }), (page) => ({ kind: 'page', page }));
194
+ };
195
+ /** Loads one detail selected from the retained validated page. */
196
+ const loadDetail = (userId) => {
197
+ const selectedOrganization = organizationId;
198
+ const operations = options.readOperations();
199
+ const previous = projection(state);
200
+ const page = previous?.kind === 'page' ? previous.page : previous?.page;
201
+ const selected = page?.data.find((row) => row.id === userId);
202
+ if (!selectedOrganization || !operations || !page || !selected)
203
+ return Promise.resolve();
204
+ return startRead(() => operations.get(selectedOrganization, userId), (value) => ({
205
+ kind: 'detail',
206
+ page,
207
+ selected,
208
+ detail: value.detail,
209
+ etag: value.etag,
210
+ }));
211
+ };
212
+ /** Reloads the current target or page after a definite mutation success. */
213
+ const reconcile = (previous, deleted = false) => {
214
+ if (!previous || previous.kind === 'page' || deleted) {
215
+ return loadPage();
216
+ }
217
+ return loadDetail(previous.selected.id);
218
+ };
219
+ /** Applies a fixed mutation result without optimistic local patching. */
220
+ const finishMutation = async (result, previous, noReadSuccess, deleted = false) => {
221
+ if (result.kind === 'session-invalid') {
222
+ sessionInvalid();
223
+ return;
224
+ }
225
+ if (result.kind === 'success') {
226
+ setUserRecoveryRequired(false);
227
+ const current = options.readState();
228
+ if (current.kind === 'authenticated' && current.capabilities.canReadUsers) {
229
+ await reconcile(previous, deleted);
230
+ }
231
+ else if (noReadSuccess) {
232
+ visible = true;
233
+ publish({ kind: 'success', action: noReadSuccess });
234
+ syncMount();
235
+ }
236
+ return;
237
+ }
238
+ if (result.kind === 'outcome-unknown') {
239
+ const current = options.readState();
240
+ setUserRecoveryRequired(current.kind === 'authenticated' && current.capabilities.canReadUsers);
241
+ visible = true;
242
+ publish(withOutcome(previous, 'outcome-unknown'));
243
+ syncMount();
244
+ return;
245
+ }
246
+ if (deleted) {
247
+ await reconcile(previous, false);
248
+ if (options.readState().kind !== 'authenticated')
249
+ return;
250
+ visible = true;
251
+ publish(withOutcome(projection(state), result.failure));
252
+ }
253
+ else {
254
+ visible = true;
255
+ publish(withOutcome(previous, result.failure));
256
+ }
257
+ syncMount();
258
+ };
259
+ /** Opens one typed dialog and dispatches at most one resulting mutation. */
260
+ const runMutation = async (open, choose, requiredCapability, noReadSuccess, deleted = false) => {
261
+ if (disposed || operation || dialogBusy || options.isApplicationBusy())
262
+ return;
263
+ const current = options.readState();
264
+ if (current.kind !== 'authenticated' || !current.organization)
265
+ return;
266
+ if (!current.capabilities[requiredCapability])
267
+ return;
268
+ if ((userRecoveryRequired || roleRecoveryRequired) && current.capabilities.canReadUsers)
269
+ return;
270
+ const controller = new AbortController();
271
+ operation = controller;
272
+ operationPrevious = projection(state);
273
+ mutationDispatched = false;
274
+ dialogBusy = true;
275
+ options.setDialogBusy(true);
276
+ const contextGeneration = generation;
277
+ const capturedOperation = ++operationGeneration;
278
+ try {
279
+ const choice = await open(controller.signal);
280
+ if (!owns(contextGeneration, capturedOperation, controller))
281
+ return;
282
+ const latest = options.readState();
283
+ if (latest.kind !== 'authenticated' ||
284
+ latest.organization?.id !== organizationId ||
285
+ !latest.capabilities[requiredCapability])
286
+ return;
287
+ const request = choose(choice);
288
+ if (!request)
289
+ return;
290
+ mutationDispatched = true;
291
+ const result = await request;
292
+ if (!owns(contextGeneration, capturedOperation, controller))
293
+ return;
294
+ operation = undefined;
295
+ mutationDispatched = false;
296
+ if (dialogBusy) {
297
+ dialogBusy = false;
298
+ options.setDialogBusy(false);
299
+ }
300
+ const previous = operationPrevious;
301
+ operationPrevious = undefined;
302
+ await finishMutation(result, previous, noReadSuccess, deleted);
303
+ }
304
+ finally {
305
+ releaseOperation(controller);
306
+ }
307
+ };
308
+ /** Returns the currently validated selected-user projection. */
309
+ const selection = () => {
310
+ const current = projection(state);
311
+ return current?.kind === 'detail' || current?.kind === 'history' ? current : undefined;
312
+ };
313
+ /** Dispatches one already-confirmed selected-user mutation. */
314
+ const selectedMutation = (open, choose, requiredCapability, deleted = false) => {
315
+ const selected = selection();
316
+ const operations = options.readOperations();
317
+ const selectedOrganization = organizationId;
318
+ if (!selected || !operations || !selectedOrganization)
319
+ return;
320
+ void runMutation((signal) => open(selected, signal), (choice) => choose(selected, choice, operations, selectedOrganization), requiredCapability, undefined, deleted);
321
+ };
322
+ /** Opens the selected user's direct role assignments through the feature-local owner. */
323
+ const openUserRoles = () => {
324
+ if (dialogBusy || operation || options.isApplicationBusy())
325
+ return;
326
+ dialogBusy = true;
327
+ options.setDialogBusy(true);
328
+ const workflow = openAdminUserRoleWorkflow({
329
+ host: options.host,
330
+ readState: options.readState,
331
+ readSelection: selection,
332
+ readSessionEpoch: () => currentSessionEpoch,
333
+ readOperations: () => options.readRbacOperations?.(),
334
+ readApplicationOperations: () => options.readApplicationOperations?.(),
335
+ setRecoveryRequired: setRoleRecoveryRequired,
336
+ requestAuthentication: sessionInvalid,
337
+ onClosed: () => {
338
+ userRoleWorkflow = undefined;
339
+ if (!dialogBusy)
340
+ return;
341
+ dialogBusy = false;
342
+ options.setDialogBusy(false);
343
+ workspace?.focusCurrent();
344
+ },
345
+ });
346
+ if (workflow)
347
+ userRoleWorkflow = workflow;
348
+ else {
349
+ dialogBusy = false;
350
+ options.setDialogBusy(false);
351
+ }
352
+ };
353
+ /** Handles one closed workspace intent. */
354
+ const handleIntent = (intent) => {
355
+ const current = options.readState();
356
+ if (!organizationId ||
357
+ !options.readOperations() ||
358
+ disposed ||
359
+ current.kind !== 'authenticated' ||
360
+ current.organization?.id !== organizationId)
361
+ return;
362
+ const requiredCapability = intent.kind === 'delete'
363
+ ? 'canDeleteUsers'
364
+ : intent.kind === 'roles'
365
+ ? 'canReadRoles'
366
+ : intent.kind === 'edit' ||
367
+ intent.kind === 'set-password' ||
368
+ intent.kind === 'clear-password' ||
369
+ intent.kind === 'verify-email'
370
+ ? 'canUpdateUsers'
371
+ : intent.kind === 'deactivate' || intent.kind === 'activate'
372
+ ? 'canManageUserLifecycle'
373
+ : 'canReadUsers';
374
+ if (intent.kind !== 'back' && !current.capabilities[requiredCapability])
375
+ return;
376
+ if (intent.kind === 'search') {
377
+ query = {
378
+ ...query,
379
+ page: 1,
380
+ ...(intent.value ? { search: intent.value } : { search: undefined }),
381
+ };
382
+ loadPage();
383
+ return;
384
+ }
385
+ if (intent.kind === 'filter') {
386
+ query = {
387
+ ...query,
388
+ page: 1,
389
+ ...(intent.status ? { status: intent.status } : { status: undefined }),
390
+ };
391
+ loadPage();
392
+ return;
393
+ }
394
+ if (intent.kind === 'page') {
395
+ query = { ...query, page: intent.page };
396
+ loadPage();
397
+ return;
398
+ }
399
+ if (intent.kind === 'select') {
400
+ loadDetail(intent.userId);
401
+ return;
402
+ }
403
+ if (intent.kind === 'back') {
404
+ cancelRead();
405
+ const previous = projection(state);
406
+ if (previous?.kind === 'history') {
407
+ publish({
408
+ kind: 'detail',
409
+ page: previous.page,
410
+ selected: previous.selected,
411
+ detail: previous.detail,
412
+ etag: previous.etag,
413
+ });
414
+ }
415
+ else if (previous?.kind === 'detail') {
416
+ publish({ kind: 'page', page: previous.page });
417
+ }
418
+ return;
419
+ }
420
+ if (intent.kind === 'retry') {
421
+ const previous = projection(state);
422
+ if (previous?.kind === 'detail' || previous?.kind === 'history')
423
+ loadDetail(previous.selected.id);
424
+ else
425
+ loadPage();
426
+ return;
427
+ }
428
+ if (intent.kind === 'history') {
429
+ const selected = selection();
430
+ const selectedOrganization = organizationId;
431
+ const operations = options.readOperations();
432
+ if (!operations)
433
+ return;
434
+ if (!selected)
435
+ return;
436
+ void startRead(() => operations.getHistory(selectedOrganization, selected.selected.id), (history) => ({ ...selected, kind: 'history', history }), false);
437
+ return;
438
+ }
439
+ if (intent.kind === 'roles') {
440
+ openUserRoles();
441
+ return;
442
+ }
443
+ if (intent.kind === 'edit') {
444
+ selectedMutation((selected, signal) => dialogs.edit(options.host, signal, selected.detail), (selected, choice, operations, selectedOrganization) => {
445
+ return choice.kind === 'update'
446
+ ? operations.update(selectedOrganization, selected.selected.id, choice.input, selected.etag ?? undefined)
447
+ : undefined;
448
+ }, 'canUpdateUsers');
449
+ return;
450
+ }
451
+ if (intent.kind === 'set-password') {
452
+ selectedMutation((selected, signal) => dialogs.setPassword(options.host, signal, selected.detail.email), (selected, choice, operations, selectedOrganization) => {
453
+ return choice.kind === 'set-password'
454
+ ? operations.setPassword(selectedOrganization, selected.selected.id, choice.input)
455
+ : undefined;
456
+ }, 'canUpdateUsers');
457
+ return;
458
+ }
459
+ if (intent.kind === 'delete') {
460
+ const organization = current.organization;
461
+ selectedMutation((selected, signal) => dialogs.delete(options.host, signal, organization, selected.detail), (selected, choice, operations, selectedOrganization) => choice.kind === 'delete'
462
+ ? operations.delete(selectedOrganization, selected.selected.id)
463
+ : undefined, 'canDeleteUsers', true);
464
+ return;
465
+ }
466
+ const action = intent.kind;
467
+ if (action === 'clear-password' ||
468
+ action === 'verify-email' ||
469
+ action === 'deactivate' ||
470
+ action === 'activate') {
471
+ selectedMutation((selected, signal) => dialogs.confirm(options.host, signal, action, selected.detail.email), (selected, choice, operations, selectedOrganization) => {
472
+ if (choice.kind === 'cancel')
473
+ return undefined;
474
+ const userId = selected.selected.id;
475
+ if (choice.kind === 'clear-password')
476
+ return operations.clearPassword(selectedOrganization, userId);
477
+ if (choice.kind === 'verify-email')
478
+ return operations.verifyEmail(selectedOrganization, userId);
479
+ if (choice.kind === 'deactivate')
480
+ return operations.deactivate(selectedOrganization, userId);
481
+ return operations.activate(selectedOrganization, userId);
482
+ }, action === 'clear-password' || action === 'verify-email'
483
+ ? 'canUpdateUsers'
484
+ : 'canManageUserLifecycle');
485
+ }
486
+ };
487
+ /** Creates a workspace for the current exact capability snapshot. */
488
+ const replaceWorkspace = (connection) => {
489
+ workspace?.dispose();
490
+ workspace = makeWorkspace({
491
+ capabilities: connection.capabilities,
492
+ onIntent: handleIntent,
493
+ focusView: (view) => options.host.loop.focusView(view),
494
+ });
495
+ workspace.setState(state);
496
+ syncMount();
497
+ };
498
+ /** Cancels the owned operation and preserves only a truthful same-context projection. */
499
+ const cancelActiveOperation = () => {
500
+ if (userRoleWorkflow) {
501
+ closeUserRoles();
502
+ return;
503
+ }
504
+ if (!operation)
505
+ return;
506
+ const previous = operationPrevious;
507
+ const indeterminate = mutationDispatched;
508
+ operationGeneration += 1;
509
+ operation.abort();
510
+ const controller = operation;
511
+ operation = undefined;
512
+ mutationDispatched = false;
513
+ operationPrevious = undefined;
514
+ if (dialogBusy) {
515
+ dialogBusy = false;
516
+ options.setDialogBusy(false);
517
+ }
518
+ if (indeterminate) {
519
+ const current = options.readState();
520
+ setUserRecoveryRequired(current.kind === 'authenticated' && current.capabilities.canReadUsers);
521
+ visible = true;
522
+ publish(withOutcome(previous, 'outcome-unknown'));
523
+ syncMount();
524
+ }
525
+ else if (previous)
526
+ publish(previous);
527
+ releaseOperation(controller);
528
+ };
529
+ return {
530
+ syncContext: (connection, sessionEpoch) => {
531
+ const valid = connection.kind === 'authenticated' && connection.organization;
532
+ if (!valid || !options.readOperations()) {
533
+ closeUserRoles();
534
+ currentSessionEpoch = sessionEpoch;
535
+ generation += 1;
536
+ operationGeneration += 1;
537
+ operation?.abort();
538
+ operation = undefined;
539
+ operationPrevious = undefined;
540
+ mutationDispatched = false;
541
+ contextKey = undefined;
542
+ organizationId = undefined;
543
+ if (dialogBusy) {
544
+ dialogBusy = false;
545
+ options.setDialogBusy(false);
546
+ }
547
+ clearRecoveryRequired();
548
+ visible = false;
549
+ workspace?.clear();
550
+ workspace?.dispose();
551
+ workspace = undefined;
552
+ state = { kind: 'closed' };
553
+ syncMount();
554
+ return;
555
+ }
556
+ const nextKey = `${sessionEpoch}:${connection.organization.id}`;
557
+ const nextCapabilities = JSON.stringify(connection.capabilities);
558
+ if (nextKey !== contextKey) {
559
+ closeUserRoles();
560
+ currentSessionEpoch = sessionEpoch;
561
+ generation += 1;
562
+ operationGeneration += 1;
563
+ operation?.abort();
564
+ operation = undefined;
565
+ operationPrevious = undefined;
566
+ mutationDispatched = false;
567
+ contextKey = nextKey;
568
+ organizationId = connection.organization.id;
569
+ capabilitiesKey = nextCapabilities;
570
+ if (dialogBusy) {
571
+ dialogBusy = false;
572
+ options.setDialogBusy(false);
573
+ }
574
+ clearRecoveryRequired();
575
+ visible = false;
576
+ workspace?.clear();
577
+ state = { kind: 'closed' };
578
+ query = { page: 1 };
579
+ replaceWorkspace(connection);
580
+ }
581
+ else if (nextCapabilities !== capabilitiesKey) {
582
+ closeUserRoles();
583
+ currentSessionEpoch = sessionEpoch;
584
+ capabilitiesKey = nextCapabilities;
585
+ cancelActiveOperation();
586
+ replaceWorkspace(connection);
587
+ }
588
+ },
589
+ handleCommand: (command) => {
590
+ if (command !== ADMIN_COMMANDS.browseUsers &&
591
+ command !== ADMIN_COMMANDS.createUser &&
592
+ command !== ADMIN_COMMANDS.inviteUser)
593
+ return false;
594
+ const current = options.readState();
595
+ const operations = options.readOperations();
596
+ if (current.kind !== 'authenticated' || !current.organization || !operations)
597
+ return true;
598
+ if (command === ADMIN_COMMANDS.browseUsers) {
599
+ if (!current.capabilities.canReadUsers)
600
+ return true;
601
+ visible = true;
602
+ syncMount();
603
+ query = { page: 1 };
604
+ loadPage();
605
+ return true;
606
+ }
607
+ if (command === ADMIN_COMMANDS.createUser) {
608
+ if (!current.capabilities.canCreateUsers)
609
+ return true;
610
+ const selectedOrganization = current.organization.id;
611
+ void runMutation((signal) => dialogs.create(options.host, signal), (choice) => {
612
+ return choice.kind === 'create'
613
+ ? operations.create(selectedOrganization, choice.input)
614
+ : undefined;
615
+ }, 'canCreateUsers', 'created');
616
+ return true;
617
+ }
618
+ if (!current.capabilities.canInviteUsers)
619
+ return true;
620
+ const selectedOrganization = current.organization.id;
621
+ void runMutation((signal) => dialogs.invite(options.host, signal, async (input) => {
622
+ const result = await operations.previewInvitation(selectedOrganization, input);
623
+ if (signal.aborted)
624
+ return { kind: 'failure', failure: 'unavailable' };
625
+ if (result.kind !== 'session-invalid')
626
+ return result;
627
+ sessionInvalid();
628
+ return { kind: 'failure', failure: 'unauthorized' };
629
+ }), (choice) => {
630
+ return choice.kind === 'invite'
631
+ ? operations.invite(selectedOrganization, choice.input)
632
+ : undefined;
633
+ }, 'canInviteUsers', 'invited');
634
+ return true;
635
+ },
636
+ cancelActiveOperation,
637
+ handleRecoverableGeometry: (nextRecoverable) => {
638
+ if (recoverable === nextRecoverable)
639
+ return;
640
+ recoverable = nextRecoverable;
641
+ if (!recoverable)
642
+ cancelActiveOperation();
643
+ syncMount(!recoverable);
644
+ if (recoverable && visible) {
645
+ const recoveryGeneration = generation;
646
+ queueMicrotask(() => {
647
+ if (disposed || !recoverable || generation !== recoveryGeneration)
648
+ return;
649
+ workspace?.setState(state);
650
+ workspace?.focusCurrent();
651
+ });
652
+ }
653
+ },
654
+ dispose: () => {
655
+ if (disposed)
656
+ return;
657
+ closeUserRoles();
658
+ disposed = true;
659
+ generation += 1;
660
+ operationGeneration += 1;
661
+ operation?.abort();
662
+ operation = undefined;
663
+ if (dialogBusy) {
664
+ dialogBusy = false;
665
+ options.setDialogBusy(false);
666
+ }
667
+ workspace?.clear();
668
+ workspace?.dispose();
669
+ workspace = undefined;
670
+ options.mountWorkspace(null);
671
+ },
672
+ };
673
+ }
674
+ //# sourceMappingURL=user-controller.js.map