redmine-context 1.0.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 (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +449 -0
  3. package/dist/bundle/index.d.ts +5 -0
  4. package/dist/bundle/index.js +5 -0
  5. package/dist/bundle/json.d.ts +90 -0
  6. package/dist/bundle/json.js +266 -0
  7. package/dist/bundle/markdown.d.ts +75 -0
  8. package/dist/bundle/markdown.js +294 -0
  9. package/dist/bundle/search-list.d.ts +43 -0
  10. package/dist/bundle/search-list.js +53 -0
  11. package/dist/bundle/stable-stringify.d.ts +26 -0
  12. package/dist/bundle/stable-stringify.js +50 -0
  13. package/dist/cache/contract.d.ts +157 -0
  14. package/dist/cache/contract.js +0 -0
  15. package/dist/cache/disk-index.d.ts +82 -0
  16. package/dist/cache/disk-index.js +220 -0
  17. package/dist/cache/disk.d.ts +133 -0
  18. package/dist/cache/disk.js +313 -0
  19. package/dist/cache/gc.d.ts +78 -0
  20. package/dist/cache/gc.js +123 -0
  21. package/dist/cache/get-or-compute.d.ts +36 -0
  22. package/dist/cache/get-or-compute.js +52 -0
  23. package/dist/cache/index.d.ts +9 -0
  24. package/dist/cache/index.js +8 -0
  25. package/dist/cache/keys.d.ts +76 -0
  26. package/dist/cache/keys.js +78 -0
  27. package/dist/cache/memory.d.ts +48 -0
  28. package/dist/cache/memory.js +110 -0
  29. package/dist/cache-first.d.ts +127 -0
  30. package/dist/cache-first.js +227 -0
  31. package/dist/client/errors.d.ts +33 -0
  32. package/dist/client/errors.js +49 -0
  33. package/dist/client/http.d.ts +110 -0
  34. package/dist/client/http.js +207 -0
  35. package/dist/client/index.d.ts +5 -0
  36. package/dist/client/index.js +5 -0
  37. package/dist/client/issues.d.ts +71 -0
  38. package/dist/client/issues.js +100 -0
  39. package/dist/client/search.d.ts +58 -0
  40. package/dist/client/search.js +81 -0
  41. package/dist/config/credentials.d.ts +247 -0
  42. package/dist/config/credentials.js +427 -0
  43. package/dist/config/doctor.d.ts +123 -0
  44. package/dist/config/doctor.js +260 -0
  45. package/dist/config/index.d.ts +6 -0
  46. package/dist/config/index.js +6 -0
  47. package/dist/config/keyring.d.ts +96 -0
  48. package/dist/config/keyring.js +158 -0
  49. package/dist/config/login.d.ts +97 -0
  50. package/dist/config/login.js +189 -0
  51. package/dist/config/settings.d.ts +94 -0
  52. package/dist/config/settings.js +140 -0
  53. package/dist/contract.d.ts +173 -0
  54. package/dist/contract.js +27 -0
  55. package/dist/core.d.ts +1 -0
  56. package/dist/core.js +8 -0
  57. package/dist/extract/audio-extractor.d.ts +105 -0
  58. package/dist/extract/audio-extractor.js +156 -0
  59. package/dist/extract/audio.d.ts +126 -0
  60. package/dist/extract/audio.js +184 -0
  61. package/dist/extract/dispatcher.d.ts +132 -0
  62. package/dist/extract/dispatcher.js +115 -0
  63. package/dist/extract/download.d.ts +111 -0
  64. package/dist/extract/download.js +261 -0
  65. package/dist/extract/duration.d.ts +106 -0
  66. package/dist/extract/duration.js +148 -0
  67. package/dist/extract/ffmpeg.d.ts +56 -0
  68. package/dist/extract/ffmpeg.js +95 -0
  69. package/dist/extract/gguf.d.ts +137 -0
  70. package/dist/extract/gguf.js +215 -0
  71. package/dist/extract/index.d.ts +19 -0
  72. package/dist/extract/index.js +19 -0
  73. package/dist/extract/magic.d.ts +80 -0
  74. package/dist/extract/magic.js +282 -0
  75. package/dist/extract/ooxml.d.ts +131 -0
  76. package/dist/extract/ooxml.js +336 -0
  77. package/dist/extract/pdf.d.ts +147 -0
  78. package/dist/extract/pdf.js +322 -0
  79. package/dist/extract/queue.d.ts +167 -0
  80. package/dist/extract/queue.js +217 -0
  81. package/dist/extract/subprocess.d.ts +145 -0
  82. package/dist/extract/subprocess.js +181 -0
  83. package/dist/extract/tesseract.d.ts +153 -0
  84. package/dist/extract/tesseract.js +321 -0
  85. package/dist/extract/video-extractor.d.ts +84 -0
  86. package/dist/extract/video-extractor.js +89 -0
  87. package/dist/extract/video.d.ts +198 -0
  88. package/dist/extract/video.js +418 -0
  89. package/dist/extract/which.d.ts +63 -0
  90. package/dist/extract/which.js +72 -0
  91. package/dist/extract/whisper-extract.d.ts +211 -0
  92. package/dist/extract/whisper-extract.js +323 -0
  93. package/dist/extract/whisper.d.ts +50 -0
  94. package/dist/extract/whisper.js +67 -0
  95. package/dist/extract/zip.d.ts +50 -0
  96. package/dist/extract/zip.js +165 -0
  97. package/dist/extract-issue-attachments.d.ts +82 -0
  98. package/dist/extract-issue-attachments.js +156 -0
  99. package/dist/fetch-attachment-text.d.ts +113 -0
  100. package/dist/fetch-attachment-text.js +155 -0
  101. package/dist/fetch-issue-bundle.d.ts +81 -0
  102. package/dist/fetch-issue-bundle.js +93 -0
  103. package/dist/fetch-issue-search.d.ts +74 -0
  104. package/dist/fetch-issue-search.js +120 -0
  105. package/dist/index.d.ts +21 -0
  106. package/dist/index.js +67 -0
  107. package/dist/normalize/collections.d.ts +52 -0
  108. package/dist/normalize/collections.js +170 -0
  109. package/dist/normalize/helpers.d.ts +35 -0
  110. package/dist/normalize/helpers.js +59 -0
  111. package/dist/normalize/index.d.ts +2 -0
  112. package/dist/normalize/index.js +2 -0
  113. package/dist/normalize/issue.d.ts +34 -0
  114. package/dist/normalize/issue.js +154 -0
  115. package/dist/surfaces/cli/commands.d.ts +61 -0
  116. package/dist/surfaces/cli/commands.js +262 -0
  117. package/dist/surfaces/cli/main.d.ts +32 -0
  118. package/dist/surfaces/cli/main.js +210 -0
  119. package/dist/surfaces/cli/prompts.d.ts +66 -0
  120. package/dist/surfaces/cli/prompts.js +147 -0
  121. package/dist/surfaces/cli/tty.d.ts +27 -0
  122. package/dist/surfaces/cli/tty.js +35 -0
  123. package/dist/surfaces/cli/types.d.ts +39 -0
  124. package/dist/surfaces/cli/types.js +7 -0
  125. package/dist/surfaces/mcp/server.d.ts +171 -0
  126. package/dist/surfaces/mcp/server.js +427 -0
  127. package/dist/surfaces/tui/app.d.ts +55 -0
  128. package/dist/surfaces/tui/app.js +180 -0
  129. package/dist/surfaces/tui/attachment-status.d.ts +79 -0
  130. package/dist/surfaces/tui/attachment-status.js +113 -0
  131. package/dist/surfaces/tui/components/breadcrumb.d.ts +7 -0
  132. package/dist/surfaces/tui/components/breadcrumb.js +31 -0
  133. package/dist/surfaces/tui/components/gradient-text.d.ts +23 -0
  134. package/dist/surfaces/tui/components/gradient-text.js +75 -0
  135. package/dist/surfaces/tui/components/scroll-view.d.ts +25 -0
  136. package/dist/surfaces/tui/components/scroll-view.js +77 -0
  137. package/dist/surfaces/tui/components/spinner.d.ts +12 -0
  138. package/dist/surfaces/tui/components/spinner.js +39 -0
  139. package/dist/surfaces/tui/components/text-input.d.ts +45 -0
  140. package/dist/surfaces/tui/components/text-input.js +114 -0
  141. package/dist/surfaces/tui/format-file-size.d.ts +24 -0
  142. package/dist/surfaces/tui/format-file-size.js +43 -0
  143. package/dist/surfaces/tui/glyphs.d.ts +47 -0
  144. package/dist/surfaces/tui/glyphs.js +84 -0
  145. package/dist/surfaces/tui/hooks/use-auth-guard.d.ts +39 -0
  146. package/dist/surfaces/tui/hooks/use-auth-guard.js +135 -0
  147. package/dist/surfaces/tui/hooks/use-doctor-status.d.ts +64 -0
  148. package/dist/surfaces/tui/hooks/use-doctor-status.js +123 -0
  149. package/dist/surfaces/tui/hooks/use-escape-interceptor.d.ts +25 -0
  150. package/dist/surfaces/tui/hooks/use-escape-interceptor.js +65 -0
  151. package/dist/surfaces/tui/hooks/use-exit-guard.d.ts +18 -0
  152. package/dist/surfaces/tui/hooks/use-exit-guard.js +66 -0
  153. package/dist/surfaces/tui/hooks/use-export-bundle.d.ts +62 -0
  154. package/dist/surfaces/tui/hooks/use-export-bundle.js +100 -0
  155. package/dist/surfaces/tui/hooks/use-issue-detail.d.ts +61 -0
  156. package/dist/surfaces/tui/hooks/use-issue-detail.js +132 -0
  157. package/dist/surfaces/tui/hooks/use-issue-search.d.ts +71 -0
  158. package/dist/surfaces/tui/hooks/use-issue-search.js +168 -0
  159. package/dist/surfaces/tui/hooks/use-list-navigation.d.ts +44 -0
  160. package/dist/surfaces/tui/hooks/use-list-navigation.js +82 -0
  161. package/dist/surfaces/tui/hooks/use-media-binaries.d.ts +24 -0
  162. package/dist/surfaces/tui/hooks/use-media-binaries.js +44 -0
  163. package/dist/surfaces/tui/hooks/use-my-issues.d.ts +66 -0
  164. package/dist/surfaces/tui/hooks/use-my-issues.js +151 -0
  165. package/dist/surfaces/tui/hooks/use-onboarding-callbacks.d.ts +11 -0
  166. package/dist/surfaces/tui/hooks/use-onboarding-callbacks.js +104 -0
  167. package/dist/surfaces/tui/hooks/use-terminal-width.d.ts +52 -0
  168. package/dist/surfaces/tui/hooks/use-terminal-width.js +90 -0
  169. package/dist/surfaces/tui/index.d.ts +50 -0
  170. package/dist/surfaces/tui/index.js +158 -0
  171. package/dist/surfaces/tui/instance.d.ts +37 -0
  172. package/dist/surfaces/tui/instance.js +36 -0
  173. package/dist/surfaces/tui/job-registry.d.ts +113 -0
  174. package/dist/surfaces/tui/job-registry.js +123 -0
  175. package/dist/surfaces/tui/job-status.d.ts +45 -0
  176. package/dist/surfaces/tui/job-status.js +81 -0
  177. package/dist/surfaces/tui/navigation.d.ts +74 -0
  178. package/dist/surfaces/tui/navigation.js +87 -0
  179. package/dist/surfaces/tui/palettes.d.ts +38 -0
  180. package/dist/surfaces/tui/palettes.js +244 -0
  181. package/dist/surfaces/tui/screen.d.ts +30 -0
  182. package/dist/surfaces/tui/screen.js +51 -0
  183. package/dist/surfaces/tui/screens/about.d.ts +2 -0
  184. package/dist/surfaces/tui/screens/about.js +35 -0
  185. package/dist/surfaces/tui/screens/appearance.d.ts +2 -0
  186. package/dist/surfaces/tui/screens/appearance.js +74 -0
  187. package/dist/surfaces/tui/screens/config.d.ts +2 -0
  188. package/dist/surfaces/tui/screens/config.js +82 -0
  189. package/dist/surfaces/tui/screens/doctor.d.ts +2 -0
  190. package/dist/surfaces/tui/screens/doctor.js +109 -0
  191. package/dist/surfaces/tui/screens/export.d.ts +2 -0
  192. package/dist/surfaces/tui/screens/export.js +168 -0
  193. package/dist/surfaces/tui/screens/home-selection.d.ts +70 -0
  194. package/dist/surfaces/tui/screens/home-selection.js +80 -0
  195. package/dist/surfaces/tui/screens/home.d.ts +2 -0
  196. package/dist/surfaces/tui/screens/home.js +200 -0
  197. package/dist/surfaces/tui/screens/issue-detail.d.ts +6 -0
  198. package/dist/surfaces/tui/screens/issue-detail.js +182 -0
  199. package/dist/surfaces/tui/screens/jobs.d.ts +7 -0
  200. package/dist/surfaces/tui/screens/jobs.js +89 -0
  201. package/dist/surfaces/tui/screens/loaded-issue-context.d.ts +49 -0
  202. package/dist/surfaces/tui/screens/loaded-issue-context.js +57 -0
  203. package/dist/surfaces/tui/screens/onboarding/api-key.d.ts +2 -0
  204. package/dist/surfaces/tui/screens/onboarding/api-key.js +69 -0
  205. package/dist/surfaces/tui/screens/onboarding/login.d.ts +2 -0
  206. package/dist/surfaces/tui/screens/onboarding/login.js +50 -0
  207. package/dist/surfaces/tui/screens/onboarding/mode.d.ts +2 -0
  208. package/dist/surfaces/tui/screens/onboarding/mode.js +42 -0
  209. package/dist/surfaces/tui/screens/onboarding/onboarding-context.d.ts +221 -0
  210. package/dist/surfaces/tui/screens/onboarding/onboarding-context.js +131 -0
  211. package/dist/surfaces/tui/screens/onboarding/success.d.ts +2 -0
  212. package/dist/surfaces/tui/screens/onboarding/success.js +41 -0
  213. package/dist/surfaces/tui/screens/onboarding/url.d.ts +24 -0
  214. package/dist/surfaces/tui/screens/onboarding/url.js +84 -0
  215. package/dist/surfaces/tui/screens/onboarding/validating.d.ts +1 -0
  216. package/dist/surfaces/tui/screens/onboarding/validating.js +86 -0
  217. package/dist/surfaces/tui/screens/welcome.d.ts +6 -0
  218. package/dist/surfaces/tui/screens/welcome.js +95 -0
  219. package/dist/surfaces/tui/status-color.d.ts +21 -0
  220. package/dist/surfaces/tui/status-color.js +23 -0
  221. package/dist/surfaces/tui/symbols.d.ts +231 -0
  222. package/dist/surfaces/tui/symbols.js +14 -0
  223. package/dist/surfaces/tui/terminal-colors.d.ts +29 -0
  224. package/dist/surfaces/tui/terminal-colors.js +39 -0
  225. package/dist/surfaces/tui/theme.d.ts +156 -0
  226. package/dist/surfaces/tui/theme.js +86 -0
  227. package/dist/surfaces/tui/truncate.d.ts +31 -0
  228. package/dist/surfaces/tui/truncate.js +81 -0
  229. package/package.json +93 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Estado local do fluxo de onboarding (M2-05.1, issue #27, evoluído na #28)
3
+ * URL da instância + modo de autenticação + a resolução do login, tudo
4
+ * compartilhado entre as telas do fluxo (`url.tsx` → `mode.tsx` →
5
+ * `login.tsx`/`api-key.tsx` → `validating.tsx` → `success.tsx`) sem
6
+ * prop-drilling — como toda tela do roteador é renderizada sem props
7
+ * (`../../screen.ts`), o único jeito de uma tela passar estado para a
8
+ * próxima é via contexto.
9
+ *
10
+ * Os callbacks de submissão (`onUrlSubmit`, `onModeSelect`, `onLoginSubmit`,
11
+ * `onApiKeySubmit`) são injetáveis, todos no-op por padrão — o wiring real
12
+ * (chamada a `loginWithPassword`/`validateApiKey` do core + persistência via
13
+ * `MigratingCredentialCascade`) vive em `../../hooks/use-onboarding-callbacks.ts`,
14
+ * o único ponto que importa o core para este fluxo (ADR-005); quem instancia
15
+ * o provider com os callbacks reais é `../../app.tsx`.
16
+ *
17
+ * M2-13 (issue #36) acrescenta o MODO RE-AUTH (`reAuth`/`beginReAuth`/
18
+ * `resolveReAuth`/`abortReAuth`): quando uma tela de dados (#29+) recebe um
19
+ * 401 (`RedmineAuthError`) numa chamada ao core, ela usa
20
+ * `../../hooks/use-auth-guard.ts` para empilhar `onboarding-login` de novo,
21
+ * reaproveitando este mesmo fluxo de login para o usuário reautenticar sem
22
+ * perder o que estava fazendo — ver o JSDoc de `useAuthGuard` para o
23
+ * contrato completo consumido pelas telas de dados, e `validating.tsx`/
24
+ * `api-key.tsx` para onde o sucesso do re-login é detectado e a operação
25
+ * original retomada.
26
+ *
27
+ * Fix do review do PR #119: dois 401 concorrentes (duas telas de dados, ou
28
+ * a mesma tela com duas chamadas em voo) podem cair em re-auth ao mesmo
29
+ * tempo — em vez de o segundo `beginReAuth` sobrescrever silenciosamente o
30
+ * primeiro (perdendo seu retry/reject para sempre), `reAuth` agora acumula
31
+ * uma LISTA de pendências (`pending`); uma única passagem de login bem
32
+ * sucedida atende todas elas (`resolveReAuth`). Esc durante o re-login
33
+ * (`../../app.tsx`) agora também tem uma saída explícita: `abortReAuth`
34
+ * rejeita todas as pendências em vez de deixar a(s) `Promise`(s) do
35
+ * `guard()` penduradas para sempre.
36
+ */
37
+ import { type ReactNode } from 'react';
38
+ import type { ScreenName } from '../../screen.js';
39
+ /** Modo de autenticação escolhido na tela de modo (`mode.tsx`). */
40
+ export type OnboardingMode = 'password' | 'api-key';
41
+ /** Credenciais coletadas na tela de login (`login.tsx`). */
42
+ export interface OnboardingLoginCredentials {
43
+ /** Usuário/login digitado (eco normal). */
44
+ username: string;
45
+ /** Senha digitada (mascarada na tela, nunca ecoada em texto). */
46
+ password: string;
47
+ }
48
+ /** Identidade mínima do usuário autenticado — nunca inclui a api_key. */
49
+ export interface OnboardingUser {
50
+ /** Id numérico do usuário no Redmine. */
51
+ id: number;
52
+ /** Login do usuário. */
53
+ login: string;
54
+ /** Nome de exibição (`firstname lastname`, ou `login` se ausentes). */
55
+ name: string;
56
+ }
57
+ /**
58
+ * Resultado de uma tentativa de login (por senha ou api_key colada),
59
+ * classificado pelo que a tela de validação (`validating.tsx`) precisa
60
+ * decidir: mostrar a splash de sucesso, oferecer o fallback de api_key
61
+ * (401/2FA), ou mostrar uma mensagem de erro acionável (rede/TLS).
62
+ */
63
+ export type OnboardingLoginOutcome = {
64
+ kind: 'success';
65
+ user: OnboardingUser;
66
+ } | {
67
+ kind: 'auth-error';
68
+ message: string;
69
+ } | {
70
+ kind: 'network-error';
71
+ message: string;
72
+ };
73
+ /**
74
+ * Uma pendência individual de re-auth (fix do review #119) — um par
75
+ * retry/reject correspondente a UMA chamada de `guard()`
76
+ * (`../../hooks/use-auth-guard.ts`) que recebeu 401. `OnboardingReAuth`
77
+ * acumula uma lista destas: com dois 401 concorrentes, ambas as pendências
78
+ * são atendidas por uma única passagem de login (`resolveReAuth`) ou
79
+ * canceladas juntas (`abortReAuth`).
80
+ */
81
+ export interface OnboardingReAuthPending {
82
+ /**
83
+ * Reexecuta a operação original que falhou com 401. Chamado automaticamente
84
+ * por `resolveReAuth()` quando o login é refeito com sucesso — nenhuma tela
85
+ * precisa chamá-lo diretamente.
86
+ */
87
+ retry: () => void;
88
+ /**
89
+ * Rejeita a `Promise` de `guard()` correspondente a esta pendência com o
90
+ * motivo do abandono (tipicamente `ReAuthAbortedError`,
91
+ * `../../hooks/use-auth-guard.ts`). Chamado automaticamente por
92
+ * `abortReAuth()` — nenhuma tela precisa chamá-lo diretamente.
93
+ */
94
+ reject: (reason: Error) => void;
95
+ }
96
+ /**
97
+ * Estado de um fluxo de re-autenticação em andamento (M2-13, issue #36) —
98
+ * criado por `useAuthGuard` (`../../hooks/use-auth-guard.ts`) ao capturar um
99
+ * `RedmineAuthError` (401) numa operação de uma tela de dados (#29+).
100
+ */
101
+ export interface OnboardingReAuth {
102
+ /**
103
+ * Tela que estava ativa quando o PRIMEIRO 401 deste ciclo aconteceu — a
104
+ * navegação volta para ela (`popTo`, `../../navigation.tsx`) assim que o
105
+ * re-login for concluído (com sucesso) ou abandonado (Esc). Pendências
106
+ * subsequentes (401 concorrentes) reaproveitam esta mesma origem — a TUI
107
+ * só sustenta uma tela de dados ativa por vez, então não há origem
108
+ * divergente de fato.
109
+ */
110
+ origin: ScreenName;
111
+ /**
112
+ * Todas as pendências acumuladas desde que o modo re-auth foi ativado
113
+ * (fix do review #119) — ver {@link OnboardingReAuthPending}.
114
+ */
115
+ pending: readonly OnboardingReAuthPending[];
116
+ }
117
+ /** Callbacks de submissão injetáveis — todos no-op por padrão (wiring real na #28, ver topo do arquivo). */
118
+ export interface OnboardingCallbacks {
119
+ /** Chamado quando a URL é validada (esquema) e confirmada (Enter na tela de URL). */
120
+ onUrlSubmit: (url: string) => void;
121
+ /** Chamado quando o usuário escolhe um modo de autenticação. */
122
+ onModeSelect: (mode: OnboardingMode) => void;
123
+ /**
124
+ * Chamado quando o formulário de login (usuário/senha) é confirmado.
125
+ * Resolve para o {@link OnboardingLoginOutcome} — consumido por
126
+ * `validating.tsx` via `pendingLogin`.
127
+ */
128
+ onLoginSubmit: (credentials: OnboardingLoginCredentials) => Promise<OnboardingLoginOutcome>;
129
+ /**
130
+ * Chamado quando uma api_key colada é confirmada (`api-key.tsx`) — valida
131
+ * a key contra o Redmine ANTES de salvar.
132
+ */
133
+ onApiKeySubmit: (apiKey: string) => Promise<OnboardingLoginOutcome>;
134
+ }
135
+ /** Valor exposto pelo contexto de onboarding. */
136
+ export interface OnboardingValue {
137
+ /** URL digitada na tela de URL — sobrevive à navegação para trás (Esc). */
138
+ url: string;
139
+ /** Atualiza a URL — ligado diretamente ao `onChange` do `TextInput` da tela de URL. */
140
+ setUrl: (url: string) => void;
141
+ /** Modo de autenticação escolhido, ou `undefined` antes da tela de modo. */
142
+ mode: OnboardingMode | undefined;
143
+ /** Atualiza o modo escolhido. */
144
+ setMode: (mode: OnboardingMode) => void;
145
+ /**
146
+ * Promise do login em andamento — definida por `login.tsx` ao confirmar o
147
+ * formulário (via `callbacks.onLoginSubmit`) e consumida por
148
+ * `validating.tsx`, que decide a próxima tela a partir do
149
+ * {@link OnboardingLoginOutcome} resolvido. `undefined` fora desse fluxo.
150
+ */
151
+ pendingLogin: Promise<OnboardingLoginOutcome> | undefined;
152
+ /** Atualiza `pendingLogin`. */
153
+ setPendingLogin: (pending: Promise<OnboardingLoginOutcome>) => void;
154
+ /** Usuário autenticado com sucesso — preenchido por `validating.tsx`/`api-key.tsx`, exibido em `success.tsx`. */
155
+ user: OnboardingUser | undefined;
156
+ /** Atualiza `user`. */
157
+ setUser: (user: OnboardingUser) => void;
158
+ /** Callbacks de submissão injetáveis (ver {@link OnboardingCallbacks}). */
159
+ callbacks: OnboardingCallbacks;
160
+ /**
161
+ * Fluxo de re-autenticação em andamento (M2-13, issue #36), ou `undefined`
162
+ * fora dele. Consumido por `validating.tsx`/`api-key.tsx` para desviar do
163
+ * caminho normal de sucesso (splash) e, em vez disso, retomar a operação de
164
+ * origem — ver {@link OnboardingReAuth}.
165
+ */
166
+ reAuth: OnboardingReAuth | undefined;
167
+ /**
168
+ * Inicia (ou ADICIONA a) o modo re-auth — chamado por `useAuthGuard()`
169
+ * (`../../hooks/use-auth-guard.ts`), nunca diretamente pelas telas.
170
+ *
171
+ * Fix do review #119: se já houver um `reAuth` ativo (401 concorrente),
172
+ * `pending` é apenas ACRESCENTADO à lista existente — a `origin` recebida
173
+ * aqui é ignorada nesse caso (a origem do ciclo já foi fixada na primeira
174
+ * chamada) — uma única passagem de login atende todas as pendências.
175
+ * Senão, ativa o modo com `origin` e uma lista iniciada só com `pending`.
176
+ *
177
+ * @returns `true` se este chamado ATIVOU o modo re-auth (nenhum estava em
178
+ * andamento antes) — o chamador (`useAuthGuard`) usa isso para decidir
179
+ * se deve empilhar `onboarding-login` (só na primeira vez; pendências
180
+ * subsequentes reaproveitam a tela já empilhada).
181
+ */
182
+ beginReAuth: (origin: ScreenName, pending: OnboardingReAuthPending) => boolean;
183
+ /**
184
+ * Encerra o modo re-auth com SUCESSO: dispara `retry()` de TODAS as
185
+ * pendências acumuladas (fix do review #119 — antes só a mais recente era
186
+ * atendida) e limpa o estado. Chamado por `validating.tsx`/`api-key.tsx`
187
+ * quando o re-login resolve com `{ kind: 'success' }` enquanto `reAuth`
188
+ * está definido. No-op se não houver `reAuth` em andamento.
189
+ */
190
+ resolveReAuth: () => void;
191
+ /**
192
+ * Encerra o modo re-auth com ABANDONO (fix do review #119 — Esc durante o
193
+ * re-login, ver `../../app.tsx`): rejeita TODAS as pendências acumuladas
194
+ * com `reason` (tipicamente `ReAuthAbortedError`,
195
+ * `../../hooks/use-auth-guard.ts`) e limpa o estado — sem isso, a(s)
196
+ * `Promise`(s) de `guard()` ficariam penduradas para sempre. No-op se não
197
+ * houver `reAuth` em andamento.
198
+ */
199
+ abortReAuth: (reason: Error) => void;
200
+ }
201
+ /** Props do `OnboardingProvider`. */
202
+ export interface OnboardingProviderProps {
203
+ /** Callbacks reais de submissão (injetados pela #28); no-op por padrão. */
204
+ callbacks?: OnboardingCallbacks;
205
+ children: ReactNode;
206
+ }
207
+ /**
208
+ * Provider do contexto de onboarding — instanciado uma vez em `../../app.tsx`,
209
+ * envolvendo toda a árvore de telas (não só as de onboarding), para que
210
+ * `useOnboarding()` esteja sempre disponível quando o roteador troca para
211
+ * qualquer tela `onboarding-*`.
212
+ */
213
+ export declare function OnboardingProvider({ callbacks, children }: OnboardingProviderProps): import("react").JSX.Element;
214
+ /**
215
+ * Hook de onboarding consumido pelas telas `url.tsx`/`mode.tsx`/`login.tsx`/
216
+ * `api-key.tsx`/`validating.tsx`/`success.tsx`.
217
+ *
218
+ * @throws {Error} Se chamado fora de um `<OnboardingProvider>` — indica erro
219
+ * de composição (tela renderizada sem passar pelo roteador).
220
+ */
221
+ export declare function useOnboarding(): OnboardingValue;
@@ -0,0 +1,131 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Estado local do fluxo de onboarding (M2-05.1, issue #27, evoluído na #28)
4
+ * URL da instância + modo de autenticação + a resolução do login, tudo
5
+ * compartilhado entre as telas do fluxo (`url.tsx` → `mode.tsx` →
6
+ * `login.tsx`/`api-key.tsx` → `validating.tsx` → `success.tsx`) sem
7
+ * prop-drilling — como toda tela do roteador é renderizada sem props
8
+ * (`../../screen.ts`), o único jeito de uma tela passar estado para a
9
+ * próxima é via contexto.
10
+ *
11
+ * Os callbacks de submissão (`onUrlSubmit`, `onModeSelect`, `onLoginSubmit`,
12
+ * `onApiKeySubmit`) são injetáveis, todos no-op por padrão — o wiring real
13
+ * (chamada a `loginWithPassword`/`validateApiKey` do core + persistência via
14
+ * `MigratingCredentialCascade`) vive em `../../hooks/use-onboarding-callbacks.ts`,
15
+ * o único ponto que importa o core para este fluxo (ADR-005); quem instancia
16
+ * o provider com os callbacks reais é `../../app.tsx`.
17
+ *
18
+ * M2-13 (issue #36) acrescenta o MODO RE-AUTH (`reAuth`/`beginReAuth`/
19
+ * `resolveReAuth`/`abortReAuth`): quando uma tela de dados (#29+) recebe um
20
+ * 401 (`RedmineAuthError`) numa chamada ao core, ela usa
21
+ * `../../hooks/use-auth-guard.ts` para empilhar `onboarding-login` de novo,
22
+ * reaproveitando este mesmo fluxo de login para o usuário reautenticar sem
23
+ * perder o que estava fazendo — ver o JSDoc de `useAuthGuard` para o
24
+ * contrato completo consumido pelas telas de dados, e `validating.tsx`/
25
+ * `api-key.tsx` para onde o sucesso do re-login é detectado e a operação
26
+ * original retomada.
27
+ *
28
+ * Fix do review do PR #119: dois 401 concorrentes (duas telas de dados, ou
29
+ * a mesma tela com duas chamadas em voo) podem cair em re-auth ao mesmo
30
+ * tempo — em vez de o segundo `beginReAuth` sobrescrever silenciosamente o
31
+ * primeiro (perdendo seu retry/reject para sempre), `reAuth` agora acumula
32
+ * uma LISTA de pendências (`pending`); uma única passagem de login bem
33
+ * sucedida atende todas elas (`resolveReAuth`). Esc durante o re-login
34
+ * (`../../app.tsx`) agora também tem uma saída explícita: `abortReAuth`
35
+ * rejeita todas as pendências em vez de deixar a(s) `Promise`(s) do
36
+ * `guard()` penduradas para sempre.
37
+ */
38
+ import { createContext, useCallback, useContext, useRef, useState } from 'react';
39
+ /** Callbacks padrão: no-op — usados quando `<OnboardingProvider>` não recebe `callbacks`. */
40
+ const NOOP_CALLBACKS = {
41
+ onUrlSubmit: () => undefined,
42
+ onModeSelect: () => undefined,
43
+ onLoginSubmit: () => Promise.resolve({ kind: 'network-error', message: 'onLoginSubmit não configurado.' }),
44
+ onApiKeySubmit: () => Promise.resolve({ kind: 'network-error', message: 'onApiKeySubmit não configurado.' }),
45
+ };
46
+ const OnboardingContext = createContext(undefined);
47
+ /**
48
+ * Provider do contexto de onboarding — instanciado uma vez em `../../app.tsx`,
49
+ * envolvendo toda a árvore de telas (não só as de onboarding), para que
50
+ * `useOnboarding()` esteja sempre disponível quando o roteador troca para
51
+ * qualquer tela `onboarding-*`.
52
+ */
53
+ export function OnboardingProvider({ callbacks = NOOP_CALLBACKS, children }) {
54
+ const [url, setUrl] = useState('');
55
+ const [mode, setMode] = useState(undefined);
56
+ const [pendingLogin, setPendingLogin] = useState(undefined);
57
+ const [user, setUser] = useState(undefined);
58
+ const [reAuth, setReAuth] = useState(undefined);
59
+ // Ref espelhando `reAuth` (fix do review #119): `beginReAuth` precisa
60
+ // decidir SINCRONAMENTE (para devolver o `boolean` de ativação ao
61
+ // chamador) se já existe um ciclo em andamento, mas o setter funcional do
62
+ // `useState` só executa o callback no próximo render — não dá pra
63
+ // confiar nele para essa decisão. A ref é a única fonte de verdade
64
+ // síncrona; `setReAuth` continua sendo chamado em paralelo só para
65
+ // provocar o re-render de quem lê `reAuth` do contexto.
66
+ const reAuthRef = useRef(undefined);
67
+ // Handlers estáveis (useCallback) — mesmo padrão dos demais hooks da TUI
68
+ // (ver `../../hooks/use-exit-guard.ts`): identidade fixa evita
69
+ // re-subscrições desnecessárias em quem os usa como dependência de efeito.
70
+ const beginReAuth = useCallback((origin, pending) => {
71
+ const current = reAuthRef.current;
72
+ if (current !== undefined) {
73
+ // Já em andamento (401 concorrente) — apenas acrescenta à lista,
74
+ // mantendo a origem original.
75
+ const next = { origin: current.origin, pending: [...current.pending, pending] };
76
+ reAuthRef.current = next;
77
+ setReAuth(next);
78
+ return false;
79
+ }
80
+ const next = { origin, pending: [pending] };
81
+ reAuthRef.current = next;
82
+ setReAuth(next);
83
+ return true;
84
+ }, []);
85
+ const resolveReAuth = useCallback(() => {
86
+ const current = reAuthRef.current;
87
+ reAuthRef.current = undefined;
88
+ setReAuth(undefined);
89
+ current?.pending.forEach((item) => {
90
+ item.retry();
91
+ });
92
+ }, []);
93
+ const abortReAuth = useCallback((reason) => {
94
+ const current = reAuthRef.current;
95
+ reAuthRef.current = undefined;
96
+ setReAuth(undefined);
97
+ current?.pending.forEach((item) => {
98
+ item.reject(reason);
99
+ });
100
+ }, []);
101
+ const value = {
102
+ url,
103
+ setUrl,
104
+ mode,
105
+ setMode,
106
+ pendingLogin,
107
+ setPendingLogin,
108
+ user,
109
+ setUser,
110
+ callbacks,
111
+ reAuth,
112
+ beginReAuth,
113
+ resolveReAuth,
114
+ abortReAuth,
115
+ };
116
+ return _jsx(OnboardingContext.Provider, { value: value, children: children });
117
+ }
118
+ /**
119
+ * Hook de onboarding consumido pelas telas `url.tsx`/`mode.tsx`/`login.tsx`/
120
+ * `api-key.tsx`/`validating.tsx`/`success.tsx`.
121
+ *
122
+ * @throws {Error} Se chamado fora de um `<OnboardingProvider>` — indica erro
123
+ * de composição (tela renderizada sem passar pelo roteador).
124
+ */
125
+ export function useOnboarding() {
126
+ const value = useContext(OnboardingContext);
127
+ if (value === undefined) {
128
+ throw new Error('useOnboarding() usado fora de <OnboardingProvider>.');
129
+ }
130
+ return value;
131
+ }
@@ -0,0 +1,2 @@
1
+ /** Tela de onboarding: sucesso — `Enter` segue para a home (pilha zerada via `resetTo`). */
2
+ export declare function OnboardingSuccessScreen(): import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Tela de onboarding: sucesso (#28) — splash comemorativa mostrando o usuário
4
+ * autenticado (`OnboardingContext.user`, preenchido por `validating.tsx`/
5
+ * `api-key.tsx` ao concluir com sucesso).
6
+ *
7
+ * Único lugar da TUI onde um capricho visual mais forte é permitido pelo
8
+ * guideline do tema (`../../theme.tsx`): borda DUPLA + `theme.accent` em
9
+ * destaque, para marcar visualmente a conclusão do fluxo — em qualquer outra
10
+ * tela isso seria decoração em excesso (o guideline pede moderação com
11
+ * `accent`), mas aqui é o clímax de um fluxo que só acontece uma vez por
12
+ * instância configurada.
13
+ *
14
+ * `Enter` segue para a home (M2-06, #29) com a PILHA ZERADA (`resetTo`) —
15
+ * sem telas do onboarding sobrando por baixo para um Esc alcançar. A home é
16
+ * o destino pós-onboarding: lista "minhas issues" com a credencial recém
17
+ * salva.
18
+ */
19
+ import { Box, Text, useInput } from 'ink';
20
+ import { useCallback, useRef } from 'react';
21
+ import { useNavigation } from '../../navigation.js';
22
+ import { symbols } from '../../symbols.js';
23
+ import { useTheme } from '../../theme.js';
24
+ import { useOnboarding } from './onboarding-context.js';
25
+ /** Tela de onboarding: sucesso — `Enter` segue para a home (pilha zerada via `resetTo`). */
26
+ export function OnboardingSuccessScreen() {
27
+ const theme = useTheme();
28
+ const { resetTo } = useNavigation();
29
+ const { user } = useOnboarding();
30
+ // Handler ESTÁVEL (useCallback + refs, padrão do repo — ver text-input.tsx/app.tsx):
31
+ // identidade nova a cada render des/re-subscreve o useInput e pode perder um Enter rápido.
32
+ const resetToRef = useRef(resetTo);
33
+ resetToRef.current = resetTo;
34
+ const handleInput = useCallback((_input, key) => {
35
+ if (key.return) {
36
+ resetToRef.current('home');
37
+ }
38
+ }, []);
39
+ useInput(handleInput);
40
+ return (_jsxs(Box, { flexDirection: "column", alignItems: "center", paddingX: 3, paddingY: 1, borderStyle: "double", borderColor: theme.accent, children: [_jsxs(Text, { color: theme.accent, children: [symbols.star, " Login conclu\u00EDdo com sucesso! ", symbols.star] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.primary, children: [symbols.tick, " Bem-vindo", user !== undefined ? `, ${user.name}` : '', "!"] }) }), user !== undefined ? (_jsx(Box, { children: _jsxs(Text, { color: theme.muted, children: ["Usu\u00E1rio: ", user.login] }) })) : null, _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.muted, children: ["Pressione", ' ', _jsx(Text, { color: theme.accent, children: "Enter" }), ' ', "para come\u00E7ar."] }) })] }));
41
+ }
@@ -0,0 +1,24 @@
1
+ /** Resultado da classificação de uma URL digitada (ver {@link classifyUrl}). */
2
+ export type UrlClassification = {
3
+ kind: 'valid';
4
+ } | {
5
+ kind: 'insecure-allowed';
6
+ } | {
7
+ kind: 'insecure-blocked';
8
+ } | {
9
+ kind: 'invalid';
10
+ };
11
+ /**
12
+ * Classifica a URL digitada pelo esquema (`https://`/`http://`) — parsing
13
+ * completo da URL fica para a #28.
14
+ *
15
+ * @param value - Valor bruto do campo (ainda não `trim()`ado).
16
+ * @param insecureEnvSet - `true` quando `REDMINE_INSECURE` está definida no
17
+ * ambiente (ver {@link isInsecureEnvSet}).
18
+ * @example
19
+ * classifyUrl('https://redmine.example', false); // { kind: 'valid' }
20
+ * classifyUrl('http://redmine.example', false); // { kind: 'insecure-blocked' }
21
+ */
22
+ export declare function classifyUrl(value: string, insecureEnvSet: boolean): UrlClassification;
23
+ /** Tela de onboarding: URL — Enter confirma (avança para `onboarding-mode`), Esc volta (atalho global, ver `../../app.tsx`). */
24
+ export declare function OnboardingUrlScreen(): import("react").JSX.Element;
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Tela de onboarding 1/3: URL da instância Redmine (M2-05.1, issue #27).
4
+ *
5
+ * Valida apenas o ESQUEMA da URL ao confirmar (Enter) — a validação real
6
+ * (URL bem formada, instância alcançável) fica para a #28, que chama o core
7
+ * via os callbacks injetáveis do `OnboardingContext`. `http://` só é aceito
8
+ * quando `REDMINE_INSECURE` já está definida no ambiente do processo (`1`
9
+ * ou `true`, case-insensitive — mesma convenção de `../../../mcp/server.ts`
10
+ * e da flag `--insecure` da CLI) — e, mesmo aceito, dispara um aviso em
11
+ * `theme.warning` porque o tráfego (incluindo a api_key futura) não é
12
+ * criptografado.
13
+ */
14
+ import { Box, Text } from 'ink';
15
+ import { useState } from 'react';
16
+ import { TextInput } from '../../components/text-input.js';
17
+ import { useNavigation } from '../../navigation.js';
18
+ import { symbols } from '../../symbols.js';
19
+ import { useTheme } from '../../theme.js';
20
+ import { useOnboarding } from './onboarding-context.js';
21
+ /**
22
+ * Interpreta `REDMINE_INSECURE` (`1`/`true`, case-insensitive) — mesma
23
+ * convenção usada pelo MCP (`../../../mcp/server.ts`).
24
+ */
25
+ function isInsecureEnvSet(env) {
26
+ const raw = env.REDMINE_INSECURE;
27
+ return raw !== undefined && /^(1|true)$/i.test(raw.trim());
28
+ }
29
+ /**
30
+ * Classifica a URL digitada pelo esquema (`https://`/`http://`) — parsing
31
+ * completo da URL fica para a #28.
32
+ *
33
+ * @param value - Valor bruto do campo (ainda não `trim()`ado).
34
+ * @param insecureEnvSet - `true` quando `REDMINE_INSECURE` está definida no
35
+ * ambiente (ver {@link isInsecureEnvSet}).
36
+ * @example
37
+ * classifyUrl('https://redmine.example', false); // { kind: 'valid' }
38
+ * classifyUrl('http://redmine.example', false); // { kind: 'insecure-blocked' }
39
+ */
40
+ export function classifyUrl(value, insecureEnvSet) {
41
+ const trimmed = value.trim();
42
+ if (trimmed.startsWith('https://') && trimmed.length > 'https://'.length) {
43
+ return { kind: 'valid' };
44
+ }
45
+ if (trimmed.startsWith('http://') && trimmed.length > 'http://'.length) {
46
+ return insecureEnvSet ? { kind: 'insecure-allowed' } : { kind: 'insecure-blocked' };
47
+ }
48
+ return { kind: 'invalid' };
49
+ }
50
+ /** Tela de onboarding: URL — Enter confirma (avança para `onboarding-mode`), Esc volta (atalho global, ver `../../app.tsx`). */
51
+ export function OnboardingUrlScreen() {
52
+ const { push } = useNavigation();
53
+ const theme = useTheme();
54
+ const { url, setUrl, callbacks } = useOnboarding();
55
+ const [feedback, setFeedback] = useState(undefined);
56
+ function handleSubmit(value) {
57
+ const classification = classifyUrl(value, isInsecureEnvSet(process.env));
58
+ const trimmed = value.trim();
59
+ if (classification.kind === 'valid' || classification.kind === 'insecure-allowed') {
60
+ setFeedback(classification.kind === 'insecure-allowed'
61
+ ? {
62
+ level: 'warning',
63
+ message: 'AVISO: conexão insegura (http://) habilitada via REDMINE_INSECURE — o tráfego não é criptografado.',
64
+ }
65
+ : undefined);
66
+ setUrl(trimmed);
67
+ callbacks.onUrlSubmit(trimmed);
68
+ push('onboarding-mode');
69
+ return;
70
+ }
71
+ if (classification.kind === 'insecure-blocked') {
72
+ setFeedback({
73
+ level: 'danger',
74
+ message: 'Conexão http:// recusada: TLS é obrigatório. Use https:// ou defina REDMINE_INSECURE (não recomendado) para permitir.',
75
+ });
76
+ return;
77
+ }
78
+ setFeedback({
79
+ level: 'danger',
80
+ message: 'Informe uma URL começando com https:// (ex.: https://redmine.example).',
81
+ });
82
+ }
83
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, borderStyle: "round", borderColor: theme.border, children: [_jsx(Text, { color: theme.primary, children: "URL do Redmine" }), _jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: theme.muted, children: [symbols.pointer, " "] }), _jsx(TextInput, { value: url, onChange: setUrl, onSubmit: handleSubmit, placeholder: "https://redmine.example" })] }), feedback !== undefined ? (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: feedback.level === 'danger' ? theme.danger : theme.warning, children: [feedback.level === 'danger' ? symbols.cross : symbols.warning, " ", feedback.message] }) })) : null, _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.muted, children: ["Pressione", ' ', _jsx(Text, { color: theme.accent, children: "Enter" }), ' ', "para confirmar,", ' ', _jsx(Text, { color: theme.accent, children: "Esc" }), ' ', "para voltar."] }) })] }));
84
+ }
@@ -0,0 +1 @@
1
+ export declare function OnboardingValidatingScreen(): import("react").JSX.Element;
@@ -0,0 +1,86 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Tela de onboarding: validando o login por senha (#28) — consome a
4
+ * `pendingLogin` (promise) guardada em `OnboardingContext` por `login.tsx` ao
5
+ * confirmar o formulário, mostra um spinner enquanto ela resolve, e decide a
6
+ * próxima tela a partir do `OnboardingLoginOutcome`:
7
+ *
8
+ * - `success` → grava `user` no contexto e segue para a splash (`replace`,
9
+ * troca o topo da pilha — a tela de validação é transitória, não faz
10
+ * sentido voltar a ela).
11
+ * - `auth-error` (401/2FA, ADR-003) → oferece o fallback de colar api_key
12
+ * (`replace('onboarding-api-key')`).
13
+ * - `network-error` (rede/TLS) → fica nesta tela mostrando a mensagem em
14
+ * `theme.danger`, sem stack, com a próxima ação sugerida; `Esc` (atalho
15
+ * global, `../../app.tsx`) volta para o formulário de login para tentar de
16
+ * novo.
17
+ *
18
+ * M2-13 (issue #36): quando o sucesso acontece com um fluxo de re-auth em
19
+ * andamento (`OnboardingContext.reAuth`, empilhado por
20
+ * `../../hooks/use-auth-guard.ts` após um 401 numa tela de dados), o caminho
21
+ * de sucesso é DIFERENTE — em vez da splash, a navegação volta à tela de
22
+ * origem (`popTo`) e a operação original é retomada automaticamente
23
+ * (`resolveReAuth()`, que dispara o `retry` guardado no contexto).
24
+ */
25
+ import { Box, Text } from 'ink';
26
+ import { useEffect, useState } from 'react';
27
+ import { Spinner } from '../../components/spinner.js';
28
+ import { useNavigation } from '../../navigation.js';
29
+ import { symbols } from '../../symbols.js';
30
+ import { useTheme } from '../../theme.js';
31
+ import { useOnboarding } from './onboarding-context.js';
32
+ /** Sugestão de próxima ação para erros de rede/TLS (ex.: CA interna). */
33
+ const NETWORK_ERROR_HINT = 'Se a instância usa uma CA interna, configure NODE_EXTRA_CA_CERTS apontando para o certificado. Verifique também a URL e a conexão de rede.';
34
+ export function OnboardingValidatingScreen() {
35
+ const theme = useTheme();
36
+ const { replace, popTo } = useNavigation();
37
+ const { pendingLogin, setUser, reAuth, resolveReAuth } = useOnboarding();
38
+ const [networkError, setNetworkError] = useState(undefined);
39
+ useEffect(() => {
40
+ if (pendingLogin === undefined) {
41
+ return;
42
+ }
43
+ let cancelled = false;
44
+ pendingLogin
45
+ .then((outcome) => {
46
+ if (cancelled) {
47
+ return;
48
+ }
49
+ if (outcome.kind === 'success') {
50
+ setUser(outcome.user);
51
+ if (reAuth !== undefined) {
52
+ // M2-13 (#36): volta à tela de origem e retoma a operação
53
+ // original — não passa pela splash (esse re-login é invisível
54
+ // para o fluxo da tela de dados que o disparou).
55
+ popTo(reAuth.origin);
56
+ resolveReAuth();
57
+ return;
58
+ }
59
+ replace('onboarding-success');
60
+ return;
61
+ }
62
+ if (outcome.kind === 'auth-error') {
63
+ replace('onboarding-api-key');
64
+ return;
65
+ }
66
+ setNetworkError(outcome.message);
67
+ })
68
+ .catch((cause) => {
69
+ if (cancelled) {
70
+ return;
71
+ }
72
+ // Reason: defesa em profundidade — `onLoginSubmit` (ver
73
+ // `../../hooks/use-onboarding-callbacks.ts`) já converte qualquer
74
+ // falha num `OnboardingLoginOutcome` resolvido, mas um callback
75
+ // injetado incorretamente (ex.: em teste) pode rejeitar a promise.
76
+ setNetworkError(cause instanceof Error ? cause.message : String(cause));
77
+ });
78
+ return () => {
79
+ cancelled = true;
80
+ };
81
+ }, [pendingLogin, replace, popTo, reAuth, resolveReAuth, setUser]);
82
+ if (networkError !== undefined) {
83
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, borderStyle: "round", borderColor: theme.border, children: [_jsxs(Text, { color: theme.danger, children: [symbols.cross, " N\u00E3o foi poss\u00EDvel entrar"] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.danger, children: networkError }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.danger, children: NETWORK_ERROR_HINT }) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.muted, children: ["Pressione", ' ', _jsx(Text, { color: theme.accent, children: "Esc" }), ' ', "para voltar e tentar novamente."] }) })] }));
84
+ }
85
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, borderStyle: "round", borderColor: theme.border, children: _jsxs(Text, { color: theme.primary, children: [_jsx(Spinner, {}), " Validando credenciais..."] }) }));
86
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Tela inicial do roteador: `Enter` vai para `home` (credencial já salva) ou
3
+ * inicia o onboarding (M2-05.1), `?` atalhos, `d` doctor, `c` config, `q`
4
+ * sai (ver `app.tsx`).
5
+ */
6
+ export declare function WelcomeScreen(): import("react").JSX.Element;