@portaidentity/cli 1.7.3 → 1.8.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 +5 -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 +484 -0
  78. package/dist/admin/client-service.js.map +1 -0
  79. package/dist/admin/client-state.d.ts +157 -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 +484 -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 +87 -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,932 @@
1
+ /** Lifecycle and command routing for one embedded administration application. */
2
+ import { janusTheme, resolveCapabilities } from '@jsvision/core';
3
+ import { Commands, createApplication, createKeymap } from '@jsvision/ui';
4
+ import { normalizeServerOrigin } from '../global-options.js';
5
+ import { createAdminDialogSurface, createAdminInteraction, runNativeAdminApplication, } from './application-runtime.js';
6
+ import { showAuthenticationGate, showCreateOrganizationDialog, showDeleteOrganizationDialog, showOrganizationChooser, showWhoAmIDialog, } from './organization-dialogs.js';
7
+ import { createAdminOrganizationController } from './organization-controller.js';
8
+ import { createAdminOrganizationWorkspace } from './organization-workspace.js';
9
+ import { createAdminPortabilityController } from './portability-controller.js';
10
+ import { createAdminSystemConfigController } from './system-config-controller.js';
11
+ import { createAdminApplicationClientFeatures } from './application-client-features.js';
12
+ import { createAdminUserController } from './user-controller.js';
13
+ import { ADMIN_COMMANDS, createAdminPresentation } from './presentation.js';
14
+ import { canRetryAdminState, } from './state.js';
15
+ /** Signal-to-shell exit mappings used on supported POSIX hosts. */
16
+ const POSIX_SIGNAL_CODES = {
17
+ SIGHUP: 129,
18
+ SIGINT: 130,
19
+ SIGTERM: 143,
20
+ };
21
+ /** Converts an arbitrary application result to a documented process outcome. */
22
+ function normalizeExitCode(value) {
23
+ return value === 1 || value === 2 || value === 129 || value === 130 || value === 143 ? value : 0;
24
+ }
25
+ /** Starts one administration application and always releases its resources. */
26
+ export async function runAdminApplication(options) {
27
+ let server = options.server ? normalizeServerOrigin(options.server) : undefined;
28
+ const initialState = options.initialState ??
29
+ (server ? { kind: 'unauthenticated', server } : { kind: 'selecting-server' });
30
+ const caps = resolveCapabilities().profile;
31
+ const presentation = createAdminPresentation(initialState, options.showInsecureWarning ?? options.insecure, options.viewport, caps.unicode.utf8);
32
+ let currentController;
33
+ let organizationDialogOpen = false;
34
+ let userDialogOpen = false;
35
+ let authenticationGateOpen = false;
36
+ let identityDialogOpen = false;
37
+ let disposed = false;
38
+ let emitDeferredQuit = () => undefined;
39
+ let deferredQuit = false;
40
+ let userController;
41
+ let organizationController;
42
+ let applicationClientFeatures;
43
+ let portabilityController;
44
+ let systemConfigController;
45
+ let userRecoveryRequired = false;
46
+ let featureDialogOpen = false;
47
+ let sessionEpoch = initialState.kind === 'authenticated' ? 1 : 0;
48
+ const standardKeymap = createKeymap({
49
+ 'ctrl+r': ADMIN_COMMANDS.reauthenticate,
50
+ 'alt+x': Commands.quit,
51
+ });
52
+ const applicationKeymap = {
53
+ // Dialogs handle Cancel themselves, but not the global Quit command. Convert Alt-X to Cancel for
54
+ // the active modal, then re-emit Quit after that modal has left the event loop's modal scope.
55
+ lookup: (event) => {
56
+ const command = standardKeymap.lookup(event);
57
+ const modalWorkOpen = currentController !== undefined ||
58
+ authenticationGateOpen ||
59
+ organizationDialogOpen ||
60
+ identityDialogOpen ||
61
+ userDialogOpen ||
62
+ featureDialogOpen ||
63
+ organizationController?.isOpen() ||
64
+ portabilityController?.isOpen() ||
65
+ systemConfigController?.isOpen();
66
+ const cancellableWorkOpen = currentController !== undefined ||
67
+ organizationDialogOpen ||
68
+ userDialogOpen ||
69
+ featureDialogOpen ||
70
+ organizationController?.isOpen() ||
71
+ portabilityController?.isOpen() ||
72
+ systemConfigController?.isOpen();
73
+ if (command === Commands.quit && modalWorkOpen) {
74
+ if (!deferredQuit) {
75
+ deferredQuit = true;
76
+ queueMicrotask(() => {
77
+ deferredQuit = false;
78
+ if (!disposed)
79
+ emitDeferredQuit();
80
+ });
81
+ }
82
+ return Commands.cancel;
83
+ }
84
+ // Escape remains a raw dialog key unless an application operation currently owns cancellation.
85
+ return event.key === 'escape' && cancellableWorkOpen ? ADMIN_COMMANDS.cancel : command;
86
+ },
87
+ };
88
+ const factory = options.applicationFactory ?? createApplication;
89
+ const application = factory({
90
+ content: presentation.content,
91
+ menuBar: presentation.menu,
92
+ statusLine: presentation.status,
93
+ viewport: options.viewport,
94
+ systemClipboard: false,
95
+ caps,
96
+ theme: janusTheme,
97
+ keymap: applicationKeymap,
98
+ });
99
+ emitDeferredQuit = () => application.loop.emitCommand(Commands.quit);
100
+ // Keep a stable background focus target so menus remain keyboard-accessible after a modal closes.
101
+ application.loop.focusInto(presentation.content);
102
+ const dialogSurface = createAdminDialogSurface(application, presentation);
103
+ const dialogHost = dialogSurface.host;
104
+ const interaction = createAdminInteraction(application, dialogHost);
105
+ let finalized = false;
106
+ let initialized = false;
107
+ let currentOperationFallback;
108
+ let authenticationGateGeneration = 0;
109
+ let organizationGeneration = 0;
110
+ let createRecoveryRequired = false;
111
+ let deferAuthenticationGateOnce = false;
112
+ let session = options.session;
113
+ let signalExitCode;
114
+ /** Updates presentation and command availability together. */
115
+ const setState = (state) => {
116
+ if (disposed)
117
+ return;
118
+ presentation.setState(state);
119
+ organizationController?.syncContext(state, sessionEpoch);
120
+ userController?.syncContext(state, sessionEpoch);
121
+ applicationClientFeatures?.syncContext(state, sessionEpoch);
122
+ portabilityController?.syncContext(state, sessionEpoch);
123
+ systemConfigController?.syncContext(state, sessionEpoch);
124
+ const organizationWorkspaceOpen = organizationController?.isOpen() ?? false;
125
+ const portabilityWorkspaceOpen = (portabilityController?.isOpen() ?? false) || (systemConfigController?.isOpen() ?? false);
126
+ application.loop.enableCommand(ADMIN_COMMANDS.authenticate, state.kind === 'unauthenticated');
127
+ application.loop.enableCommand(ADMIN_COMMANDS.retry, canRetryAdminState(state));
128
+ application.loop.enableCommand(ADMIN_COMMANDS.reauthenticate, state.kind === 'authenticated' || state.kind === 'unauthorized');
129
+ application.loop.enableCommand(ADMIN_COMMANDS.whoAmI, state.kind === 'authenticated');
130
+ application.loop.enableCommand(ADMIN_COMMANDS.createOrganization, state.kind === 'authenticated' &&
131
+ state.capabilities.canCreateOrganizations &&
132
+ !createRecoveryRequired &&
133
+ !organizationDialogOpen &&
134
+ !currentController &&
135
+ !userDialogOpen &&
136
+ !organizationWorkspaceOpen &&
137
+ !portabilityWorkspaceOpen);
138
+ application.loop.enableCommand(ADMIN_COMMANDS.switchOrganization, state.kind === 'authenticated' &&
139
+ state.capabilities.canReadOrganizations &&
140
+ !organizationDialogOpen &&
141
+ !currentController &&
142
+ !userDialogOpen &&
143
+ !organizationWorkspaceOpen &&
144
+ !portabilityWorkspaceOpen);
145
+ application.loop.enableCommand(ADMIN_COMMANDS.manageOrganization, state.kind === 'authenticated' &&
146
+ Boolean(state.organization) &&
147
+ state.capabilities.canReadOrganizations &&
148
+ Boolean(session?.organizationWorkspace) &&
149
+ !currentController &&
150
+ !organizationDialogOpen &&
151
+ !identityDialogOpen &&
152
+ !userDialogOpen &&
153
+ !featureDialogOpen &&
154
+ !organizationWorkspaceOpen &&
155
+ !portabilityWorkspaceOpen);
156
+ application.loop.enableCommand(ADMIN_COMMANDS.browseUsers, state.kind === 'authenticated' &&
157
+ Boolean(state.organization) &&
158
+ state.capabilities.canReadUsers &&
159
+ !userDialogOpen &&
160
+ !organizationDialogOpen &&
161
+ !currentController &&
162
+ !organizationWorkspaceOpen &&
163
+ !portabilityWorkspaceOpen);
164
+ application.loop.enableCommand(ADMIN_COMMANDS.createUser, state.kind === 'authenticated' &&
165
+ Boolean(state.organization) &&
166
+ state.capabilities.canCreateUsers &&
167
+ !userDialogOpen &&
168
+ !organizationDialogOpen &&
169
+ !currentController &&
170
+ !userRecoveryRequired &&
171
+ !organizationWorkspaceOpen &&
172
+ !portabilityWorkspaceOpen);
173
+ application.loop.enableCommand(ADMIN_COMMANDS.inviteUser, state.kind === 'authenticated' &&
174
+ Boolean(state.organization) &&
175
+ state.capabilities.canInviteUsers &&
176
+ !userDialogOpen &&
177
+ !organizationDialogOpen &&
178
+ !currentController &&
179
+ !userRecoveryRequired &&
180
+ !organizationWorkspaceOpen &&
181
+ !portabilityWorkspaceOpen);
182
+ application.loop.enableCommand(ADMIN_COMMANDS.cancel, currentController !== undefined ||
183
+ organizationDialogOpen ||
184
+ userDialogOpen ||
185
+ featureDialogOpen ||
186
+ organizationWorkspaceOpen ||
187
+ portabilityWorkspaceOpen);
188
+ const featureIdle = !currentController &&
189
+ !organizationDialogOpen &&
190
+ !identityDialogOpen &&
191
+ !userDialogOpen &&
192
+ !featureDialogOpen &&
193
+ !organizationWorkspaceOpen &&
194
+ !portabilityWorkspaceOpen;
195
+ application.loop.enableCommand(ADMIN_COMMANDS.browseApplications, state.kind === 'authenticated' && state.capabilities.canReadApplications && featureIdle);
196
+ application.loop.enableCommand(ADMIN_COMMANDS.createApplication, state.kind === 'authenticated' && state.capabilities.canCreateApplications && featureIdle);
197
+ application.loop.enableCommand(ADMIN_COMMANDS.browseClients, state.kind === 'authenticated' &&
198
+ Boolean(state.organization) &&
199
+ state.capabilities.canReadClients &&
200
+ featureIdle);
201
+ application.loop.enableCommand(ADMIN_COMMANDS.createClient, state.kind === 'authenticated' &&
202
+ Boolean(state.organization) &&
203
+ state.capabilities.canCreateClients &&
204
+ state.capabilities.canReadApplications &&
205
+ featureIdle);
206
+ application.loop.enableCommand(ADMIN_COMMANDS.portability, state.kind === 'authenticated' &&
207
+ (state.capabilities.canExportData || state.capabilities.canImportData) &&
208
+ Boolean(session?.portability) &&
209
+ featureIdle);
210
+ application.loop.enableCommand(ADMIN_COMMANDS.systemConfig, state.kind === 'authenticated' &&
211
+ state.capabilities.canReadConfig === true &&
212
+ Boolean(session?.systemConfig) &&
213
+ featureIdle);
214
+ syncAuthenticationGate(state);
215
+ };
216
+ /** Builds authenticated state without retaining a stale organization failure. */
217
+ const authenticatedState = (state, organization, organizationFailure) => ({
218
+ kind: 'authenticated',
219
+ server: state.server,
220
+ identity: state.identity,
221
+ capabilities: state.capabilities,
222
+ ...(organization ? { organization } : {}),
223
+ ...(organizationFailure ? { organizationFailure } : {}),
224
+ });
225
+ /** Returns true while a logical organization operation may still publish a result. */
226
+ const ownsOrganizationGeneration = (generation) => !disposed && organizationDialogOpen && generation === organizationGeneration;
227
+ /** Closes the current organization modal and rejects all of its late continuations. */
228
+ const cancelOrganizationWork = () => {
229
+ if (!organizationDialogOpen)
230
+ return;
231
+ organizationGeneration += 1;
232
+ organizationDialogOpen = false;
233
+ application.loop.endModal(Commands.cancel);
234
+ dialogSurface.removeAll();
235
+ setState(presentation.getState());
236
+ };
237
+ /** Closes the read-only identity dialog before a synchronous terminal redraw. */
238
+ const cancelIdentityDialog = () => {
239
+ if (!identityDialogOpen)
240
+ return;
241
+ identityDialogOpen = false;
242
+ application.loop.endModal(Commands.cancel);
243
+ dialogSurface.removeAll();
244
+ setState(presentation.getState());
245
+ };
246
+ /** Closes the authentication gate and rejects its pending continuation. */
247
+ const cancelAuthenticationGate = () => {
248
+ if (!authenticationGateOpen)
249
+ return;
250
+ authenticationGateGeneration += 1;
251
+ authenticationGateOpen = false;
252
+ application.loop.endModal(Commands.cancel);
253
+ dialogSurface.removeAll();
254
+ };
255
+ /** Aborts authentication and restores its safe fallback without leaving a mounted dialog. */
256
+ const cancelSessionOperation = () => {
257
+ if (!currentController)
258
+ return;
259
+ const fallback = currentOperationFallback;
260
+ currentController.abort();
261
+ currentController = undefined;
262
+ currentOperationFallback = undefined;
263
+ dialogSurface.removeAll();
264
+ if (fallback)
265
+ setState(fallback);
266
+ else if (server)
267
+ setState({ kind: 'unauthenticated', server });
268
+ else
269
+ setState(presentation.getState());
270
+ };
271
+ /** Cancels whichever single modal or operation currently owns the application surface. */
272
+ const cancelModalWork = (preserveConfigDrafts = false) => {
273
+ cancelAuthenticationGate();
274
+ cancelIdentityDialog();
275
+ cancelOrganizationWork();
276
+ cancelSessionOperation();
277
+ organizationController?.close();
278
+ userController?.cancelActiveOperation();
279
+ applicationClientFeatures?.cancelActiveOperation();
280
+ portabilityController?.cancelActiveOperation();
281
+ if (!preserveConfigDrafts)
282
+ systemConfigController?.cancelActiveOperation();
283
+ dialogSurface.removeAll();
284
+ };
285
+ /** Opens the gate exactly once when the application is usable but not authenticated. */
286
+ function syncAuthenticationGate(state = presentation.getState()) {
287
+ const recoverable = presentation.content.bounds.width >= 32 && presentation.content.bounds.height >= 8;
288
+ if (!initialized ||
289
+ disposed ||
290
+ state.kind !== 'unauthenticated' ||
291
+ !recoverable ||
292
+ currentController ||
293
+ authenticationGateOpen ||
294
+ organizationDialogOpen ||
295
+ identityDialogOpen ||
296
+ userDialogOpen ||
297
+ featureDialogOpen) {
298
+ return;
299
+ }
300
+ if (deferAuthenticationGateOnce) {
301
+ deferAuthenticationGateOnce = false;
302
+ setTimeout(() => syncAuthenticationGate(), 0);
303
+ return;
304
+ }
305
+ const generation = ++authenticationGateGeneration;
306
+ authenticationGateOpen = true;
307
+ void showAuthenticationGate(dialogHost)
308
+ .then((choice) => {
309
+ if (disposed || !authenticationGateOpen || generation !== authenticationGateGeneration) {
310
+ return;
311
+ }
312
+ authenticationGateOpen = false;
313
+ if (choice === 'quit') {
314
+ application.loop.emitCommand(Commands.quit);
315
+ return;
316
+ }
317
+ if (session?.authenticate)
318
+ startOperation(session.authenticate);
319
+ else
320
+ syncAuthenticationGate();
321
+ })
322
+ .catch(() => {
323
+ if (generation === authenticationGateGeneration) {
324
+ authenticationGateOpen = false;
325
+ application.loop.emitCommand(Commands.quit);
326
+ }
327
+ });
328
+ }
329
+ /** Enters the established unauthenticated state after a final organization 401. */
330
+ const invalidateSession = () => {
331
+ sessionEpoch += 1;
332
+ createRecoveryRequired = false;
333
+ cancelOrganizationWork();
334
+ const operationServer = server;
335
+ if (operationServer)
336
+ setState({ kind: 'unauthenticated', server: operationServer });
337
+ };
338
+ /** Opens the bounded create form and owns exactly one submitted service call. */
339
+ function startCreateOrganization() {
340
+ const state = presentation.getState();
341
+ const operations = session?.organizations;
342
+ if (state.kind !== 'authenticated' ||
343
+ !state.capabilities.canCreateOrganizations ||
344
+ createRecoveryRequired ||
345
+ !operations ||
346
+ currentController ||
347
+ authenticationGateOpen ||
348
+ organizationDialogOpen ||
349
+ identityDialogOpen ||
350
+ userDialogOpen ||
351
+ disposed) {
352
+ return;
353
+ }
354
+ const generation = ++organizationGeneration;
355
+ organizationDialogOpen = true;
356
+ setState(state);
357
+ void showCreateOrganizationDialog(dialogHost)
358
+ .then(async (choice) => {
359
+ if (!ownsOrganizationGeneration(generation) || choice.kind === 'cancel')
360
+ return;
361
+ createRecoveryRequired = true;
362
+ setState(presentation.getState());
363
+ const result = await operations.create(choice.input);
364
+ if (!ownsOrganizationGeneration(generation))
365
+ return;
366
+ if (result.kind === 'session-invalid') {
367
+ invalidateSession();
368
+ return;
369
+ }
370
+ if (result.kind === 'success') {
371
+ createRecoveryRequired = false;
372
+ setState(authenticatedState(state, result.value));
373
+ return;
374
+ }
375
+ createRecoveryRequired =
376
+ result.failure === 'unavailable' || result.failure === 'invalid-response';
377
+ setState(authenticatedState(state, state.organization, result.failure));
378
+ })
379
+ .catch(() => undefined)
380
+ .finally(() => {
381
+ if (!ownsOrganizationGeneration(generation))
382
+ return;
383
+ organizationDialogOpen = false;
384
+ setState(presentation.getState());
385
+ });
386
+ }
387
+ /** Confirms and deletes one selected organization, then reloads organization choice. */
388
+ function startDeleteOrganization(target) {
389
+ const state = presentation.getState();
390
+ const operations = session?.organizations;
391
+ if (state.kind !== 'authenticated' ||
392
+ !state.capabilities.canDeleteOrganizations ||
393
+ target.isSuperAdmin ||
394
+ !operations ||
395
+ currentController ||
396
+ organizationDialogOpen ||
397
+ disposed)
398
+ return;
399
+ const generation = ++organizationGeneration;
400
+ const controller = new AbortController();
401
+ organizationDialogOpen = true;
402
+ setState(state);
403
+ void showDeleteOrganizationDialog(dialogHost, controller.signal, target)
404
+ .then(async (choice) => {
405
+ if (!ownsOrganizationGeneration(generation) || choice.kind !== 'delete')
406
+ return;
407
+ const latest = presentation.getState();
408
+ if (latest.kind !== 'authenticated' || !latest.capabilities.canDeleteOrganizations)
409
+ return;
410
+ const result = await operations.delete(target.id);
411
+ if (!ownsOrganizationGeneration(generation))
412
+ return;
413
+ if (result.kind === 'session-invalid') {
414
+ invalidateSession();
415
+ return;
416
+ }
417
+ if (result.kind === 'success') {
418
+ setState(authenticatedState(latest, latest.organization?.id === target.id ? undefined : latest.organization));
419
+ }
420
+ else {
421
+ setState(authenticatedState(latest, latest.organization, result.failure));
422
+ }
423
+ })
424
+ .catch(() => undefined)
425
+ .finally(() => {
426
+ if (!ownsOrganizationGeneration(generation))
427
+ return;
428
+ organizationDialogOpen = false;
429
+ setState(presentation.getState());
430
+ startOrganizationChooser();
431
+ });
432
+ }
433
+ /** Opens organization choice with one complete, order-preserving list request when authorized. */
434
+ function startOrganizationChooser() {
435
+ const state = presentation.getState();
436
+ const operations = session?.organizations;
437
+ if (state.kind !== 'authenticated' ||
438
+ !operations ||
439
+ currentController ||
440
+ authenticationGateOpen ||
441
+ organizationDialogOpen ||
442
+ identityDialogOpen ||
443
+ userDialogOpen ||
444
+ disposed) {
445
+ return;
446
+ }
447
+ const generation = ++organizationGeneration;
448
+ organizationDialogOpen = true;
449
+ setState(state);
450
+ const organizations = state.capabilities.canReadOrganizations
451
+ ? operations.listAll()
452
+ : undefined;
453
+ if (organizations) {
454
+ void organizations.then((result) => {
455
+ if (!ownsOrganizationGeneration(generation))
456
+ return;
457
+ if (result.kind === 'session-invalid') {
458
+ invalidateSession();
459
+ }
460
+ else if (result.kind === 'success') {
461
+ createRecoveryRequired = false;
462
+ setState(presentation.getState());
463
+ }
464
+ });
465
+ }
466
+ let next;
467
+ void showOrganizationChooser(dialogHost, {
468
+ capabilities: state.capabilities,
469
+ ...(state.organizationFailure ? { failure: state.organizationFailure } : {}),
470
+ ...(organizations ? { organizations } : {}),
471
+ })
472
+ .then((choice) => {
473
+ if (!ownsOrganizationGeneration(generation))
474
+ return;
475
+ if (choice.kind === 'switch') {
476
+ setState(authenticatedState(state, choice.organization));
477
+ }
478
+ else if (choice.kind === 'create') {
479
+ next = 'create';
480
+ }
481
+ else if (choice.kind === 'delete') {
482
+ next = { delete: choice.organization };
483
+ }
484
+ else if (choice.kind === 'reauthenticate') {
485
+ next = 'reauthenticate';
486
+ }
487
+ })
488
+ .catch(() => undefined)
489
+ .finally(() => {
490
+ if (!ownsOrganizationGeneration(generation))
491
+ return;
492
+ organizationDialogOpen = false;
493
+ setState(presentation.getState());
494
+ if (next === 'create')
495
+ startCreateOrganization();
496
+ else if (next === 'reauthenticate')
497
+ startReauthentication();
498
+ else if (next)
499
+ startDeleteOrganization(next.delete);
500
+ });
501
+ }
502
+ /** Runs at most one caller-cancellable session operation at a time. */
503
+ const startOperation = (operation, preserveCurrentState = false) => {
504
+ if (!operation ||
505
+ currentController ||
506
+ authenticationGateOpen ||
507
+ organizationDialogOpen ||
508
+ identityDialogOpen ||
509
+ userDialogOpen ||
510
+ disposed)
511
+ return;
512
+ organizationController?.close();
513
+ const operationServer = server;
514
+ if (!operationServer)
515
+ return;
516
+ const controller = new AbortController();
517
+ currentController = controller;
518
+ currentOperationFallback = preserveCurrentState ? presentation.getState() : undefined;
519
+ if (!preserveCurrentState) {
520
+ setState({ kind: 'authenticating', server: operationServer, canCancel: true });
521
+ }
522
+ else {
523
+ setState(presentation.getState());
524
+ }
525
+ let openOrganizationChoice = false;
526
+ void operation(controller.signal)
527
+ .then((state) => {
528
+ if (state && !disposed && currentController === controller && !controller.signal.aborted) {
529
+ if (state.kind === 'authenticated')
530
+ sessionEpoch += 1;
531
+ setState(state);
532
+ openOrganizationChoice = state.kind === 'authenticated' && !state.organization;
533
+ }
534
+ })
535
+ .catch(() => {
536
+ if (!disposed && currentController === controller && !controller.signal.aborted) {
537
+ setState(currentOperationFallback ?? {
538
+ kind: 'unauthenticated',
539
+ server: operationServer,
540
+ reason: 'unavailable',
541
+ });
542
+ }
543
+ })
544
+ .finally(() => {
545
+ if (currentController === controller) {
546
+ currentController = undefined;
547
+ currentOperationFallback = undefined;
548
+ setState(presentation.getState());
549
+ if (openOrganizationChoice)
550
+ startOrganizationChooser();
551
+ }
552
+ });
553
+ };
554
+ /** Reauthenticates and reconciles an existing organization before releasing operation ownership. */
555
+ function startReauthentication() {
556
+ applicationClientFeatures?.cancelActiveOperation();
557
+ const operation = session?.reauthenticate;
558
+ if (!operation ||
559
+ currentController ||
560
+ authenticationGateOpen ||
561
+ organizationDialogOpen ||
562
+ identityDialogOpen ||
563
+ userDialogOpen ||
564
+ disposed)
565
+ return;
566
+ organizationController?.close();
567
+ const operationServer = server;
568
+ if (!operationServer)
569
+ return;
570
+ const previous = presentation.getState();
571
+ const controller = new AbortController();
572
+ currentController = controller;
573
+ currentOperationFallback = previous;
574
+ setState(previous);
575
+ let openOrganizationChoice = false;
576
+ /** Releases reauthentication ownership before a follow-up chooser may open. */
577
+ const release = () => {
578
+ if (currentController !== controller)
579
+ return;
580
+ currentController = undefined;
581
+ currentOperationFallback = undefined;
582
+ setState(presentation.getState());
583
+ if (openOrganizationChoice)
584
+ startOrganizationChooser();
585
+ };
586
+ void operation(controller.signal)
587
+ .then(async (state) => {
588
+ if (!state || disposed || currentController !== controller || controller.signal.aborted) {
589
+ return;
590
+ }
591
+ if (state.kind !== 'authenticated') {
592
+ sessionEpoch += 1;
593
+ deferAuthenticationGateOnce = state.kind === 'unauthenticated';
594
+ setState(state);
595
+ release();
596
+ return;
597
+ }
598
+ sessionEpoch += 1;
599
+ if (previous.kind !== 'authenticated' || !previous.organization) {
600
+ setState(state);
601
+ openOrganizationChoice = !state.organization;
602
+ release();
603
+ return;
604
+ }
605
+ const sameSubject = state.identity.sub === previous.identity.sub;
606
+ const replacement = authenticatedState(state, sameSubject ? previous.organization : undefined);
607
+ setState(replacement);
608
+ const operations = session?.organizations;
609
+ if (!operations) {
610
+ release();
611
+ return;
612
+ }
613
+ const result = await operations.reconcile(previous.organization.id);
614
+ if (disposed || currentController !== controller || controller.signal.aborted)
615
+ return;
616
+ if (result.kind === 'session-invalid') {
617
+ createRecoveryRequired = false;
618
+ setState({ kind: 'unauthenticated', server: operationServer });
619
+ }
620
+ else if (result.kind === 'match') {
621
+ createRecoveryRequired = false;
622
+ setState(authenticatedState(state, result.organization));
623
+ }
624
+ else if (result.kind === 'absent' ||
625
+ result.kind === 'matching-invalid' ||
626
+ (result.kind === 'failure' && result.failure === 'unauthorized')) {
627
+ setState(state);
628
+ openOrganizationChoice = true;
629
+ }
630
+ else {
631
+ setState(authenticatedState(state, sameSubject ? previous.organization : undefined, result.failure));
632
+ }
633
+ })
634
+ .catch(() => {
635
+ if (!disposed && currentController === controller && !controller.signal.aborted) {
636
+ setState(previous);
637
+ }
638
+ })
639
+ .finally(release);
640
+ }
641
+ organizationController = createAdminOrganizationController({
642
+ host: dialogHost,
643
+ readState: () => presentation.getState(),
644
+ readOperations: () => session?.organizationWorkspace,
645
+ mountWorkspace: presentation.setWorkspace,
646
+ requestAuthentication: invalidateSession,
647
+ workspaceFactory: createAdminOrganizationWorkspace,
648
+ onOrganizationChange: (organization) => {
649
+ const state = presentation.getState();
650
+ if (state.kind !== 'authenticated' ||
651
+ !state.organization ||
652
+ state.organization.id !== organization.id) {
653
+ return;
654
+ }
655
+ setState(authenticatedState(state, {
656
+ id: organization.id,
657
+ name: organization.name,
658
+ slug: organization.slug,
659
+ status: organization.status,
660
+ isSuperAdmin: organization.isSuperAdmin,
661
+ }));
662
+ },
663
+ onWorkspaceClosed: () => setState(presentation.getState()),
664
+ });
665
+ userController = createAdminUserController({
666
+ host: dialogHost,
667
+ readState: () => presentation.getState(),
668
+ readOperations: () => session?.users,
669
+ readRbacOperations: () => session?.rbac,
670
+ readApplicationOperations: () => session?.applications,
671
+ mountWorkspace: presentation.setUserWorkspace,
672
+ isApplicationBusy: () => Boolean(currentController ||
673
+ authenticationGateOpen ||
674
+ organizationDialogOpen ||
675
+ identityDialogOpen ||
676
+ organizationController?.isOpen() ||
677
+ portabilityController?.isOpen() ||
678
+ systemConfigController?.isOpen()),
679
+ setDialogBusy: (busy) => {
680
+ userDialogOpen = busy;
681
+ setState(presentation.getState());
682
+ },
683
+ requestAuthentication: invalidateSession,
684
+ setRecoveryRequired: (required) => {
685
+ userRecoveryRequired = required;
686
+ setState(presentation.getState());
687
+ },
688
+ });
689
+ applicationClientFeatures = createAdminApplicationClientFeatures({
690
+ dialogs: dialogSurface,
691
+ readState: () => presentation.getState(),
692
+ readSession: () => session,
693
+ mountWorkspace: presentation.setWorkspace,
694
+ focusView: (view) => application.loop.focusInto(view),
695
+ setDialogBusy: (busy) => {
696
+ featureDialogOpen = busy;
697
+ setState(presentation.getState());
698
+ },
699
+ requestAuthentication: invalidateSession,
700
+ });
701
+ portabilityController = createAdminPortabilityController({
702
+ host: dialogHost,
703
+ readState: () => presentation.getState(),
704
+ readOperations: () => session?.portability,
705
+ readApplicationOperations: () => session?.applications,
706
+ mountWorkspace: presentation.setWorkspace,
707
+ requestAuthentication: invalidateSession,
708
+ onWorkspaceOpened: () => setState(presentation.getState()),
709
+ onWorkspaceClosed: () => setState(presentation.getState()),
710
+ });
711
+ portabilityController.syncContext(presentation.getState(), sessionEpoch);
712
+ systemConfigController = createAdminSystemConfigController({
713
+ host: {
714
+ ...dialogHost,
715
+ loop: {
716
+ execView: (view) => dialogHost.loop.execView(view),
717
+ focusView: (view) => application.loop.focusView(view),
718
+ getFocused: () => application.loop.getFocused(),
719
+ },
720
+ },
721
+ readState: () => presentation.getState(),
722
+ readOperations: () => session?.systemConfig,
723
+ mountWorkspace: presentation.setWorkspace,
724
+ requestAuthentication: invalidateSession,
725
+ onWorkspaceOpened: () => setState(presentation.getState()),
726
+ onWorkspaceClosed: () => setState(presentation.getState()),
727
+ });
728
+ systemConfigController.syncContext(presentation.getState(), sessionEpoch);
729
+ dialogSurface.setModalCommandHandler((command) => {
730
+ if (command !== ADMIN_COMMANDS.reauthenticate)
731
+ return false;
732
+ cancelModalWork();
733
+ queueMicrotask(startReauthentication);
734
+ return true;
735
+ });
736
+ const unregisterCommands = [
737
+ application.onCommand(ADMIN_COMMANDS.authenticate, () => startOperation(session?.authenticate)),
738
+ application.onCommand(ADMIN_COMMANDS.retry, () => startOperation(session?.retry)),
739
+ application.onCommand(ADMIN_COMMANDS.reauthenticate, startReauthentication),
740
+ application.onCommand(ADMIN_COMMANDS.whoAmI, () => {
741
+ const state = presentation.getState();
742
+ if (state.kind !== 'authenticated' ||
743
+ currentController ||
744
+ authenticationGateOpen ||
745
+ organizationDialogOpen ||
746
+ identityDialogOpen ||
747
+ userDialogOpen ||
748
+ disposed)
749
+ return;
750
+ identityDialogOpen = true;
751
+ void showWhoAmIDialog(dialogHost, state, options.showInsecureWarning ?? options.insecure)
752
+ .catch(() => undefined)
753
+ .finally(() => {
754
+ identityDialogOpen = false;
755
+ if (!disposed)
756
+ setState(presentation.getState());
757
+ });
758
+ }),
759
+ application.onCommand(ADMIN_COMMANDS.createOrganization, startCreateOrganization),
760
+ application.onCommand(ADMIN_COMMANDS.switchOrganization, startOrganizationChooser),
761
+ application.onCommand(ADMIN_COMMANDS.manageOrganization, () => {
762
+ organizationController?.handleCommand(ADMIN_COMMANDS.manageOrganization);
763
+ setState(presentation.getState());
764
+ }),
765
+ application.onCommand(ADMIN_COMMANDS.browseUsers, () => userController?.handleCommand(ADMIN_COMMANDS.browseUsers)),
766
+ application.onCommand(ADMIN_COMMANDS.createUser, () => userController?.handleCommand(ADMIN_COMMANDS.createUser)),
767
+ application.onCommand(ADMIN_COMMANDS.inviteUser, () => userController?.handleCommand(ADMIN_COMMANDS.inviteUser)),
768
+ application.onCommand(ADMIN_COMMANDS.browseApplications, () => applicationClientFeatures?.handleCommand(ADMIN_COMMANDS.browseApplications)),
769
+ application.onCommand(ADMIN_COMMANDS.createApplication, () => applicationClientFeatures?.handleCommand(ADMIN_COMMANDS.createApplication)),
770
+ application.onCommand(ADMIN_COMMANDS.browseClients, () => applicationClientFeatures?.handleCommand(ADMIN_COMMANDS.browseClients)),
771
+ application.onCommand(ADMIN_COMMANDS.createClient, () => applicationClientFeatures?.handleCommand(ADMIN_COMMANDS.createClient)),
772
+ application.onCommand(ADMIN_COMMANDS.portability, () => {
773
+ portabilityController?.handleCommand(ADMIN_COMMANDS.portability);
774
+ }),
775
+ application.onCommand(ADMIN_COMMANDS.systemConfig, () => {
776
+ systemConfigController?.handleCommand(ADMIN_COMMANDS.systemConfig);
777
+ }),
778
+ application.onCommand(ADMIN_COMMANDS.cancel, () => {
779
+ if (userDialogOpen) {
780
+ userController?.cancelActiveOperation();
781
+ return;
782
+ }
783
+ if (featureDialogOpen) {
784
+ applicationClientFeatures?.cancelActiveOperation();
785
+ return;
786
+ }
787
+ if (identityDialogOpen) {
788
+ cancelIdentityDialog();
789
+ return;
790
+ }
791
+ if (organizationDialogOpen) {
792
+ cancelOrganizationWork();
793
+ return;
794
+ }
795
+ if (organizationController?.isOpen()) {
796
+ organizationController.close();
797
+ setState(presentation.getState());
798
+ return;
799
+ }
800
+ if (portabilityController?.isOpen()) {
801
+ portabilityController.cancelActiveOperation();
802
+ setState(presentation.getState());
803
+ return;
804
+ }
805
+ if (systemConfigController?.isOpen()) {
806
+ systemConfigController.close();
807
+ return;
808
+ }
809
+ cancelSessionOperation();
810
+ }),
811
+ application.onCommand(Commands.quit, () => {
812
+ cancelModalWork();
813
+ }),
814
+ ];
815
+ const resizeApplication = application.loop.onResize;
816
+ application.loop.onResize = (size) => {
817
+ resizeApplication?.(size);
818
+ const recoverable = presentation.content.bounds.width >= 32 && presentation.content.bounds.height >= 8;
819
+ userController?.handleRecoverableGeometry(recoverable);
820
+ applicationClientFeatures?.handleRecoverableGeometry(recoverable);
821
+ if (!recoverable) {
822
+ cancelModalWork(true);
823
+ }
824
+ else {
825
+ syncAuthenticationGate();
826
+ }
827
+ };
828
+ setState(initialState);
829
+ /** Selects a first-run server or prepares the supplied server before verification. */
830
+ const initialize = async () => {
831
+ if (!server) {
832
+ const controller = new AbortController();
833
+ currentController = controller;
834
+ setState({ kind: 'selecting-server' });
835
+ const selected = await (options.selectServer?.(interaction, controller.signal) ??
836
+ interaction.selectServer(controller.signal));
837
+ if (controller.signal.aborted || disposed) {
838
+ if (!disposed)
839
+ application.loop.emitCommand(Commands.quit);
840
+ return;
841
+ }
842
+ currentController = undefined;
843
+ if (!selected) {
844
+ application.loop.emitCommand(Commands.quit);
845
+ return;
846
+ }
847
+ server = normalizeServerOrigin(selected);
848
+ }
849
+ const prepared = options.prepareSession?.(server, interaction);
850
+ if (prepared) {
851
+ session = prepared.session;
852
+ setState(prepared.initialState);
853
+ if (prepared.initialState.kind === 'verifying')
854
+ startOperation(session.verify, true);
855
+ return;
856
+ }
857
+ if (initialState.kind === 'verifying')
858
+ startOperation(session?.verify, true);
859
+ else if (initialState.kind === 'selecting-server')
860
+ setState({ kind: 'unauthenticated', server });
861
+ };
862
+ void initialize()
863
+ .then(() => {
864
+ initialized = true;
865
+ syncAuthenticationGate();
866
+ })
867
+ .catch(() => {
868
+ initialized = true;
869
+ if (!disposed)
870
+ setState({ kind: 'fatal', failure: { kind: 'configuration-failure' } });
871
+ });
872
+ const signalSource = options.signalSource ?? process;
873
+ const signalListeners = new Map();
874
+ const usesInjectedRunner = options.applicationRunner !== undefined;
875
+ if ((usesInjectedRunner || options.signalSource !== undefined) &&
876
+ (options.platform ?? process.platform) !== 'win32') {
877
+ for (const [signal, exitCode] of Object.entries(POSIX_SIGNAL_CODES)) {
878
+ const listener = () => {
879
+ if (signalExitCode !== undefined)
880
+ return;
881
+ signalExitCode = exitCode;
882
+ cancelModalWork();
883
+ application.loop.emitCommand(Commands.quit, exitCode);
884
+ };
885
+ signalListeners.set(signal, listener);
886
+ signalSource.once(signal, listener);
887
+ }
888
+ }
889
+ /** Removes handlers and tears down the application exactly once. */
890
+ const finalize = async () => {
891
+ if (finalized)
892
+ return;
893
+ finalized = true;
894
+ cancelModalWork();
895
+ disposed = true;
896
+ organizationController?.dispose();
897
+ organizationController = undefined;
898
+ userController?.dispose();
899
+ userController = undefined;
900
+ applicationClientFeatures?.dispose();
901
+ applicationClientFeatures = undefined;
902
+ portabilityController?.dispose();
903
+ portabilityController = undefined;
904
+ systemConfigController?.dispose();
905
+ systemConfigController = undefined;
906
+ dialogSurface.setModalCommandHandler(undefined);
907
+ for (const [signal, listener] of signalListeners)
908
+ signalSource.off(signal, listener);
909
+ for (const unregister of unregisterCommands)
910
+ unregister();
911
+ const finalizer = options.applicationFinalizer ?? ((target) => target.loop.dispose());
912
+ await finalizer(application);
913
+ };
914
+ try {
915
+ const result = await (options.applicationRunner?.(application) ??
916
+ runNativeAdminApplication(application, caps, (exitCode) => {
917
+ if (signalExitCode !== undefined)
918
+ return;
919
+ signalExitCode = normalizeExitCode(exitCode);
920
+ cancelModalWork();
921
+ application.loop.emitCommand(Commands.quit, signalExitCode);
922
+ }));
923
+ return signalExitCode ?? normalizeExitCode(result);
924
+ }
925
+ catch {
926
+ throw new Error('Administration application failed.');
927
+ }
928
+ finally {
929
+ await finalize();
930
+ }
931
+ }
932
+ //# sourceMappingURL=application.js.map