@orechio/platform-angular 0.7.4 → 0.8.1

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.
package/README.md CHANGED
@@ -28,6 +28,11 @@ provideOrechioAccount({
28
28
  { path: 'conta', children: ORECHIO_ACCOUNT_ROUTES }
29
29
  ```
30
30
 
31
+ **Provisioning (since 0.8.0).** When the BFF (`Orechio.Platform.Bff.Account` 0.5.0 or newer) returns
32
+ `provisioningRequired: true` — at login or when the session is loaded — `provideOrechioAccount` sends
33
+ the user to the first-branch route above, wherever `ORECHIO_ACCOUNT_ROUTES` is mounted. The product
34
+ writes no code for it. `SessionUser.provisioningRequired` is also available to read.
35
+
31
36
  `onCompleted` is required and validated at bootstrap — the package deliberately has no
32
37
  default destination, because a wrong one is only discovered by the first real customer
33
38
  finishing the flow, once, with no way to retry the step.
@@ -3,6 +3,7 @@ import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/cor
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormsModule } from '@angular/forms';
5
5
  import { Router } from '@angular/router';
6
+ import { SessionStore } from '@orechio/platform-angular';
6
7
  import { A as AccountClient } from './orechio-platform-angular-account-account-client-mPl0tzMg.mjs';
7
8
  import { ORECHIO_ACCOUNT_CONFIG } from './orechio-platform-angular-account.mjs';
8
9
 
@@ -10,6 +11,7 @@ class InitialBranchPage {
10
11
  router = inject(Router);
11
12
  client = inject(AccountClient);
12
13
  config = inject(ORECHIO_ACCOUNT_CONFIG);
14
+ session = inject(SessionStore);
13
15
  rotulo = this.config.branchLabel;
14
16
  nome = '';
15
17
  enviando = signal(false, /* @ts-ignore */
@@ -38,6 +40,9 @@ class InitialBranchPage {
38
40
  this.erro.set(null);
39
41
  this.client.createInitialBranch(nome).subscribe({
40
42
  next: async (resultado) => {
43
+ // A sessão em memória ainda diz `provisioningRequired: true`, e agora é falsa. O pacote
44
+ // só a descarta; quem relê é o host, pela guarda (sessão vazia = "desconhecida").
45
+ this.session.clear();
41
46
  // O pacote entrega o resultado e sai. Quem decide para onde ir é o host —
42
47
  // `nextStep` é repassado inteiro, sem interpretação (ADR-019 §3).
43
48
  const destino = this.config.onCompleted(resultado);
@@ -118,4 +123,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
118
123
  }] });
119
124
 
120
125
  export { InitialBranchPage };
121
- //# sourceMappingURL=orechio-platform-angular-account-initial-branch.page-C8a1eU3L.mjs.map
126
+ //# sourceMappingURL=orechio-platform-angular-account-initial-branch.page-BY2cxcxd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orechio-platform-angular-account-initial-branch.page-BY2cxcxd.mjs","sources":["../../../projects/platform-angular/account/src/pages/initial-branch.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { Router } from '@angular/router';\r\nimport { ProblemDetails, SessionStore } from '@orechio/platform-angular';\r\nimport { AccountClient } from '../account-client';\r\nimport { ORECHIO_ACCOUNT_CONFIG } from '../account-config';\r\n\r\n@Component({\r\n selector: 'orechio-account-initial-branch',\r\n standalone: true,\r\n imports: [FormsModule],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-account\">\r\n <ng-content select=\"[orechioAccountBrand]\"></ng-content>\r\n\r\n <h1>Configure sua primeira {{ rotulo }}</h1>\r\n <p class=\"orechio-account__muted\">\r\n É onde seus lançamentos vão nascer. Dá para criar outras depois.\r\n </p>\r\n\r\n <form (ngSubmit)=\"enviar()\">\r\n <label for=\"orechio-account-branch-name\">Nome da {{ rotulo }}</label>\r\n <input\r\n id=\"orechio-account-branch-name\"\r\n name=\"nome\"\r\n type=\"text\"\r\n autocomplete=\"organization\"\r\n [(ngModel)]=\"nome\"\r\n [disabled]=\"enviando()\"\r\n required\r\n />\r\n\r\n @if (erro()) {\r\n <p class=\"orechio-account__error\">{{ mensagemDeErro() }}</p>\r\n }\r\n\r\n <button type=\"submit\" [disabled]=\"enviando() || !nome.trim()\">\r\n {{ enviando() ? 'Criando…' : 'Concluir' }}\r\n </button>\r\n </form>\r\n </section>\r\n `,\r\n styles: `\r\n .orechio-account {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--orechio-space-3, 0.75rem);\r\n max-width: 28rem;\r\n margin: 0 auto;\r\n padding: var(--orechio-space-6, 1.5rem);\r\n color: var(--orechio-color-text, inherit);\r\n font-family: var(--orechio-font-body, inherit);\r\n }\r\n .orechio-account form { display: flex; flex-direction: column; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-account__muted { color: var(--orechio-color-text-muted, #666); }\r\n .orechio-account__error { color: var(--orechio-color-danger, #b00020); }\r\n `,\r\n})\r\nexport class InitialBranchPage {\r\n private readonly router = inject(Router);\r\n private readonly client = inject(AccountClient);\r\n private readonly config = inject(ORECHIO_ACCOUNT_CONFIG);\r\n private readonly session = inject(SessionStore);\r\n\r\n protected readonly rotulo = this.config.branchLabel;\r\n protected nome = '';\r\n protected readonly enviando = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n protected mensagemDeErro(): string {\r\n const code = this.erro()?.code;\r\n if (code === 'onboarding.branch_already_exists') return `Esta ${this.rotulo} já foi criada.`;\r\n if (code === 'session.missing') return 'Sua sessão expirou. Abra o link do e-mail novamente.';\r\n if (code === 'onboarding.not_in_provisioning') return 'Este passo já foi concluído.';\r\n if (code === 'rate_limit_exceeded') return 'Muitas tentativas. Aguarde alguns minutos.';\r\n if (code?.startsWith('branch.')) return 'Escolha um nome válido.';\r\n return 'Não foi possível concluir. Tente novamente.';\r\n }\r\n\r\n protected enviar(): void {\r\n const nome = this.nome.trim();\r\n if (!nome || this.enviando()) return;\r\n\r\n this.enviando.set(true);\r\n this.erro.set(null);\r\n\r\n this.client.createInitialBranch(nome).subscribe({\r\n next: async (resultado) => {\r\n // A sessão em memória ainda diz `provisioningRequired: true`, e agora é falsa. O pacote\r\n // só a descarta; quem relê é o host, pela guarda (sessão vazia = \"desconhecida\").\r\n this.session.clear();\r\n // O pacote entrega o resultado e sai. Quem decide para onde ir é o host —\r\n // `nextStep` é repassado inteiro, sem interpretação (ADR-019 §3).\r\n const destino = this.config.onCompleted(resultado);\r\n await this.router.navigateByUrl(destino, { replaceUrl: true });\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.erro.set(problema);\r\n this.enviando.set(false);\r\n },\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,88 @@
1
- import { InjectionToken } from '@angular/core';
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injector, effect, untracked, Injectable, InjectionToken, ENVIRONMENT_INITIALIZER } from '@angular/core';
3
+ import { Router } from '@angular/router';
4
+ import { SessionStore } from '@orechio/platform-angular';
5
+
6
+ /** O caminho da tela de primeira filial DENTRO de `ORECHIO_ACCOUNT_ROUTES`. */
7
+ const CAMINHO_RELATIVO = 'onboarding/branch';
8
+ /**
9
+ * PROV-1 (inventário A15) — leva a sessão de provisionamento à tela de primeira filial.
10
+ *
11
+ * <p>
12
+ * O BFF passa a aceitar, no login e na leitura da sessão, o token de quem ainda não tem filial
13
+ * e devolve <c>provisioningRequired: true</c>. Sem ninguém reagindo à flag, o dono entraria no
14
+ * painel com uma sessão que não abre nada. Quem reage é este serviço, ligado por
15
+ * <c>provideOrechioAccount</c> — o produto não escreve código novo, só atualiza a versão.
16
+ * </p>
17
+ *
18
+ * <p>
19
+ * <b>Por que observar a sessão e não esperar o login chamar.</b> O login e o <c>GET /session</c>
20
+ * (reload) terminam do mesmo jeito: <c>SessionStore.set</c>. Observar o store cobre os dois
21
+ * sem tocar no <c>AuthClient</c> e sem pedir um callback à tela de login do produto, que o
22
+ * pacote não possui. A navegação vem DEPOIS da que a tela de login faz ao sucesso e a
23
+ * substitui — é a última que vale.
24
+ * </p>
25
+ *
26
+ * <p>
27
+ * A flag decide navegação, nunca autorização: a Platform só aceita o token de provisionamento
28
+ * na rota de primeira filial.
29
+ * </p>
30
+ */
31
+ class ProvisioningRedirect {
32
+ session = inject(SessionStore);
33
+ router = inject(Router);
34
+ injector = inject(Injector);
35
+ iniciado = false;
36
+ start() {
37
+ if (this.iniciado)
38
+ return;
39
+ this.iniciado = true;
40
+ effect(() => {
41
+ if (this.session.user()?.provisioningRequired !== true)
42
+ return;
43
+ untracked(() => void this.irParaAPrimeiraFilial());
44
+ }, { injector: this.injector });
45
+ }
46
+ async irParaAPrimeiraFilial() {
47
+ const destino = caminhoDaPrimeiraFilial(this.router.config);
48
+ if (destino === null) {
49
+ // Sem a rota montada o desvio é impossível, e calar deixaria o dono num painel que não
50
+ // abre nada. Aviso, não exceção: lançar aqui derrubaria o login de todo mundo.
51
+ console.warn('[orechio-account] provisioningRequired recebido, mas ORECHIO_ACCOUNT_ROUTES não está ' +
52
+ `montado de forma estática (rota "${CAMINHO_RELATIVO}" não encontrada em router.config).`);
53
+ return;
54
+ }
55
+ if (this.router.url.split(/[?#]/)[0] === destino)
56
+ return;
57
+ await this.router.navigateByUrl(destino, { replaceUrl: true });
58
+ }
59
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ProvisioningRedirect, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
60
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ProvisioningRedirect, providedIn: 'root' });
61
+ }
62
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ProvisioningRedirect, decorators: [{
63
+ type: Injectable,
64
+ args: [{ providedIn: 'root' }]
65
+ }] });
66
+ /**
67
+ * Procura, em <c>router.config</c>, a rota de primeira filial e devolve o caminho ABSOLUTO dela.
68
+ * O host monta <c>ORECHIO_ACCOUNT_ROUTES</c> onde quiser (<c>conta</c>, <c>auth</c>, a raiz); o
69
+ * pacote só conhece o caminho relativo entre as telas, então o prefixo se descobre pela
70
+ * árvore. Rotas carregadas sob demanda (<c>loadChildren</c>) não estão na árvore até serem
71
+ * visitadas e não são procuradas.
72
+ */
73
+ function caminhoDaPrimeiraFilial(rotas, prefixo = '') {
74
+ for (const rota of rotas) {
75
+ const caminho = [prefixo, rota.path ?? ''].filter((parte) => parte !== '').join('/');
76
+ if (rota.path === CAMINHO_RELATIVO)
77
+ return `/${caminho}`;
78
+ if (rota.children) {
79
+ const achada = caminhoDaPrimeiraFilial(rota.children, caminho);
80
+ if (achada !== null)
81
+ return achada;
82
+ }
83
+ }
84
+ return null;
85
+ }
2
86
 
3
87
  const ORECHIO_ACCOUNT_CONFIG = new InjectionToken('ORECHIO_ACCOUNT_CONFIG');
4
88
  function provideOrechioAccount(options) {
@@ -23,7 +107,19 @@ function provideOrechioAccount(options) {
23
107
  branchLabel: options.branchLabel ?? 'unidade',
24
108
  loginPath: options.loginPath ?? '/login',
25
109
  };
26
- return [{ provide: ORECHIO_ACCOUNT_CONFIG, useValue: config }];
110
+ return [
111
+ { provide: ORECHIO_ACCOUNT_CONFIG, useValue: config },
112
+ // PROV-1: liga o desvio da sessão de provisionamento para a tela de primeira filial. Fica
113
+ // aqui, e não num guard ou callback do host, para o produto só precisar atualizar a versão.
114
+ // `ENVIRONMENT_INITIALIZER` (e não `provideEnvironmentInitializer`) para o retorno continuar
115
+ // `Provider[]`: um `EnvironmentProviders` aqui quebraria quem usa o resultado em
116
+ // `providers` de componente ou o tipa como `Provider[]`.
117
+ {
118
+ provide: ENVIRONMENT_INITIALIZER,
119
+ multi: true,
120
+ useValue: () => inject(ProvisioningRedirect).start(),
121
+ },
122
+ ];
27
123
  }
28
124
 
29
125
  /**
@@ -56,7 +152,7 @@ const ORECHIO_ACCOUNT_ROUTES = [
56
152
  {
57
153
  path: 'onboarding/branch',
58
154
  title: 'Primeira unidade',
59
- loadComponent: () => import('./orechio-platform-angular-account-initial-branch.page-C8a1eU3L.mjs').then((m) => m.InitialBranchPage),
155
+ loadComponent: () => import('./orechio-platform-angular-account-initial-branch.page-BY2cxcxd.mjs').then((m) => m.InitialBranchPage),
60
156
  },
61
157
  {
62
158
  path: 'forgot-password',
@@ -1 +1 @@
1
- {"version":3,"file":"orechio-platform-angular-account.mjs","sources":["../../../projects/platform-angular/account/src/account-config.ts","../../../projects/platform-angular/account/src/account.routes.ts","../../../projects/platform-angular/account/src/public-api.ts","../../../projects/platform-angular/account/src/orechio-platform-angular-account.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core';\r\nimport { UrlTree } from '@angular/router';\r\n\r\n/**\r\n * A fronteira do core compartilhado, como o browser a recebe (ADR-019 §3).\r\n *\r\n * <p>\r\n * Sem token nenhum, e isso é o contrato — não uma omissão. O BFF recebe os tokens da\r\n * Platform e os entrega ao mecanismo de sessão do host; o que chega aqui é o estado da\r\n * conta, nunca o material dela.\r\n * </p>\r\n */\r\nexport interface AccountCompletionResult {\r\n tenantId: string;\r\n branchId: string;\r\n applicationId: string;\r\n userId: string;\r\n /**\r\n * `OperationalReady` ou `ProductContinuation` — enum fechado do lado da Platform.\r\n * Trafega como string e é repassado **sem interpretação**: quem traduz isso em rota é o\r\n * host, nunca este pacote.\r\n */\r\n nextStep: string;\r\n}\r\n\r\n/** Rotas do BFF que o host montou. Defaults batem com `MapOrechioAccountEndpoints`. */\r\nexport interface OrechioAccountPaths {\r\n verifyEmail?: string;\r\n initialBranch?: string;\r\n forgotPassword?: string;\r\n resetPassword?: string;\r\n acceptInvite?: string;\r\n resendVerification?: string;\r\n}\r\n\r\nexport interface OrechioAccountConfig {\r\n /**\r\n * Prefixo do BFF de conta, same-origin. Nunca uma URL absoluta cross-origin: o modelo é\r\n * BFF no domínio do produto, e é isso que faz o cookie ser first-party.\r\n */\r\n basePath: string;\r\n\r\n paths: Required<OrechioAccountPaths>;\r\n\r\n /**\r\n * **O único ponto de extensão do pacote.**\r\n *\r\n * Chamado quando a conta fica operacional. O host decide para onde ir: um produto com\r\n * cadastro próprio manda para o passo dele, um produto sem passo próprio manda direto para\r\n * o painel. O pacote não conhece nenhuma dessas rotas, e é essa ignorância que o mantém\r\n * compartilhável (ADR-019 §3).\r\n *\r\n * Nem os nomes desses destinos aparecem aqui — nem em comentário. Um exemplo concreto de\r\n * um produto irmão é a primeira forma do acoplamento: vira referência, depois default,\r\n * depois `if`. A guarda de fronteira varre estes fontes e falha em termo vertical.\r\n */\r\n onCompleted: (result: AccountCompletionResult) => UrlTree | string;\r\n\r\n /**\r\n * Como o produto chama a primeira unidade operacional, no vocabulário dele. \"Unidade\" é o\r\n * default por ser o termo mais neutro que existe — cada produto tem o seu, e nenhum deles\r\n * é assunto do pacote. É por isso que isto é configuração e não literal na tela.\r\n */\r\n branchLabel: string;\r\n\r\n /** Para onde mandar quem termina um fluxo que não abre sessão (reset, convite aceito). */\r\n loginPath: string;\r\n}\r\n\r\nexport const ORECHIO_ACCOUNT_CONFIG = new InjectionToken<OrechioAccountConfig>(\r\n 'ORECHIO_ACCOUNT_CONFIG',\r\n);\r\n\r\nexport interface OrechioAccountOptions {\r\n basePath?: string;\r\n paths?: OrechioAccountPaths;\r\n onCompleted: OrechioAccountConfig['onCompleted'];\r\n branchLabel?: string;\r\n loginPath?: string;\r\n}\r\n\r\nexport function provideOrechioAccount(options: OrechioAccountOptions): Provider[] {\r\n if (typeof options?.onCompleted !== 'function') {\r\n // Falha na composição, não no primeiro cliente que termina o cadastro. Sem\r\n // `onCompleted` o pacote não teria para onde mandar ninguém, e o sintoma apareceria\r\n // exatamente no pior momento: com o usuário já cadastrado, olhando uma tela parada.\r\n throw new Error(\r\n 'provideOrechioAccount: `onCompleted` é obrigatório — é o host que decide a navegação.',\r\n );\r\n }\r\n\r\n const basePath = (options.basePath ?? '/api/account').replace(/\\/+$/, '');\r\n\r\n const config: OrechioAccountConfig = {\r\n basePath,\r\n paths: {\r\n verifyEmail: options.paths?.verifyEmail ?? '/verify-email',\r\n initialBranch: options.paths?.initialBranch ?? '/onboarding/branch',\r\n forgotPassword: options.paths?.forgotPassword ?? '/forgot-password',\r\n resetPassword: options.paths?.resetPassword ?? '/reset-password',\r\n acceptInvite: options.paths?.acceptInvite ?? '/accept-invite',\r\n resendVerification: options.paths?.resendVerification ?? '/resend-verification',\r\n },\r\n onCompleted: options.onCompleted,\r\n branchLabel: options.branchLabel ?? 'unidade',\r\n loginPath: options.loginPath ?? '/login',\r\n };\r\n\r\n return [{ provide: ORECHIO_ACCOUNT_CONFIG, useValue: config }];\r\n}\r\n","import { Routes } from '@angular/router';\r\n\r\n/**\r\n * As rotas de conta, para o host registrar ONDE ELE QUISER.\r\n *\r\n * <p>\r\n * São `Routes[]` e não um módulo com prefixo fixo de propósito: o produto monta em\r\n * `/conta`, em `/auth`, na raiz, ou espalhadas — o pacote não reivindica caminho. O que ele\r\n * fixa são os caminhos RELATIVOS entre elas, porque o `verify-email` navega para\r\n * `../onboarding/branch` quando há provisioning pendente.\r\n * </p>\r\n *\r\n * <pre>\r\n * // no host:\r\n * { path: 'conta', children: ORECHIO_ACCOUNT_ROUTES }\r\n * </pre>\r\n *\r\n * <p>\r\n * Todas com `loadComponent`: quem só usa recuperação de senha não carrega a tela de\r\n * primeira filial, e o bundle inicial do produto não paga por telas que a maioria dos\r\n * usuários vê uma vez na vida.\r\n * </p>\r\n */\r\nexport const ORECHIO_ACCOUNT_ROUTES: Routes = [\r\n {\r\n path: 'verify-email',\r\n title: 'Confirmando e-mail',\r\n loadComponent: () => import('./pages/verify-email.page').then((m) => m.VerifyEmailPage),\r\n },\r\n {\r\n path: 'onboarding/branch',\r\n title: 'Primeira unidade',\r\n loadComponent: () => import('./pages/initial-branch.page').then((m) => m.InitialBranchPage),\r\n },\r\n {\r\n path: 'forgot-password',\r\n title: 'Recuperar acesso',\r\n loadComponent: () => import('./pages/forgot-password.page').then((m) => m.ForgotPasswordPage),\r\n },\r\n {\r\n path: 'reset-password',\r\n title: 'Nova senha',\r\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\r\n // `reset` também é o default do componente; declarado aqui para a rota dizer o que faz\r\n // sem exigir que se leia a implementação.\r\n data: { modo: 'reset' },\r\n },\r\n {\r\n path: 'accept-invite',\r\n title: 'Primeiro acesso',\r\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\r\n data: { modo: 'invite' },\r\n },\r\n {\r\n path: 'resend-verification',\r\n title: 'Reenviar confirmação',\r\n loadComponent: () =>\r\n import('./pages/resend-verification.page').then((m) => m.ResendVerificationPage),\r\n },\r\n];\r\n","/*\r\n * Public API Surface of @orechio/platform-angular/account\r\n *\r\n * ACCOUNT-1C — o entry de conta. Ele traz as TELAS do onboarding compartilhado; a sessão,\r\n * o login e os interceptors continuam no entry primário, onde já estavam (ADR-019 §5.1:\r\n * login/refresh/logout são KEEP-SEPARATE — Store e Portal resolvem sessão de formas\r\n * materialmente diferentes).\r\n *\r\n * O que NÃO é exportado é parte do contrato: `AccountClient` é detalhe de implementação, e\r\n * as páginas são alcançadas por `ORECHIO_ACCOUNT_ROUTES`, nunca importadas diretamente —\r\n * exportá-las convidaria o host a montar a própria árvore de rotas por cima delas, e os\r\n * caminhos relativos entre as telas deixariam de valer.\r\n */\r\n\r\nexport type {\r\n AccountCompletionResult,\r\n OrechioAccountConfig,\r\n OrechioAccountOptions,\r\n OrechioAccountPaths,\r\n} from './account-config';\r\nexport { ORECHIO_ACCOUNT_CONFIG, provideOrechioAccount } from './account-config';\r\n\r\nexport { ORECHIO_ACCOUNT_ROUTES } from './account.routes';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;MAqEa,sBAAsB,GAAG,IAAI,cAAc,CACtD,wBAAwB;AAWpB,SAAU,qBAAqB,CAAC,OAA8B,EAAA;AAClE,IAAA,IAAI,OAAO,OAAO,EAAE,WAAW,KAAK,UAAU,EAAE;;;;AAI9C,QAAA,MAAM,IAAI,KAAK,CACb,uFAAuF,CACxF;IACH;AAEA,IAAA,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;AAEzE,IAAA,MAAM,MAAM,GAAyB;QACnC,QAAQ;AACR,QAAA,KAAK,EAAE;AACL,YAAA,WAAW,EAAE,OAAO,CAAC,KAAK,EAAE,WAAW,IAAI,eAAe;AAC1D,YAAA,aAAa,EAAE,OAAO,CAAC,KAAK,EAAE,aAAa,IAAI,oBAAoB;AACnE,YAAA,cAAc,EAAE,OAAO,CAAC,KAAK,EAAE,cAAc,IAAI,kBAAkB;AACnE,YAAA,aAAa,EAAE,OAAO,CAAC,KAAK,EAAE,aAAa,IAAI,iBAAiB;AAChE,YAAA,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,YAAY,IAAI,gBAAgB;AAC7D,YAAA,kBAAkB,EAAE,OAAO,CAAC,KAAK,EAAE,kBAAkB,IAAI,sBAAsB;AAChF,SAAA;QACD,WAAW,EAAE,OAAO,CAAC,WAAW;AAChC,QAAA,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,SAAS;AAC7C,QAAA,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,QAAQ;KACzC;IAED,OAAO,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAChE;;AC3GA;;;;;;;;;;;;;;;;;;;;AAoBG;AACI,MAAM,sBAAsB,GAAW;AAC5C,IAAA;AACE,QAAA,IAAI,EAAE,cAAc;AACpB,QAAA,KAAK,EAAE,oBAAoB;AAC3B,QAAA,aAAa,EAAE,MAAM,OAAO,mEAA2B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,eAAe,CAAC;AACxF,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,mBAAmB;AACzB,QAAA,KAAK,EAAE,kBAAkB;AACzB,QAAA,aAAa,EAAE,MAAM,OAAO,qEAA6B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AAC5F,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,iBAAiB;AACvB,QAAA,KAAK,EAAE,kBAAkB;AACzB,QAAA,aAAa,EAAE,MAAM,OAAO,sEAA8B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,kBAAkB,CAAC;AAC9F,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,gBAAgB;AACtB,QAAA,KAAK,EAAE,YAAY;AACnB,QAAA,aAAa,EAAE,MAAM,OAAO,qEAA6B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;;;AAG3F,QAAA,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;AACxB,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,eAAe;AACrB,QAAA,KAAK,EAAE,iBAAiB;AACxB,QAAA,aAAa,EAAE,MAAM,OAAO,qEAA6B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;AAC3F,QAAA,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;AACzB,KAAA;AACD,IAAA;AACE,QAAA,IAAI,EAAE,qBAAqB;AAC3B,QAAA,KAAK,EAAE,sBAAsB;AAC7B,QAAA,aAAa,EAAE,MACb,OAAO,0EAAkC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,sBAAsB,CAAC;AACnF,KAAA;;;AC1DH;;;;;;;;;;;;AAYG;;ACZH;;AAEG;;;;"}
1
+ {"version":3,"file":"orechio-platform-angular-account.mjs","sources":["../../../projects/platform-angular/account/src/provisioning-redirect.ts","../../../projects/platform-angular/account/src/account-config.ts","../../../projects/platform-angular/account/src/account.routes.ts","../../../projects/platform-angular/account/src/public-api.ts","../../../projects/platform-angular/account/src/orechio-platform-angular-account.ts"],"sourcesContent":["import { Injectable, Injector, effect, inject, untracked } from '@angular/core';\r\nimport { Route, Router } from '@angular/router';\r\nimport { SessionStore } from '@orechio/platform-angular';\r\n\r\n/** O caminho da tela de primeira filial DENTRO de `ORECHIO_ACCOUNT_ROUTES`. */\r\nconst CAMINHO_RELATIVO = 'onboarding/branch';\r\n\r\n/**\r\n * PROV-1 (inventário A15) — leva a sessão de provisionamento à tela de primeira filial.\r\n *\r\n * <p>\r\n * O BFF passa a aceitar, no login e na leitura da sessão, o token de quem ainda não tem filial\r\n * e devolve <c>provisioningRequired: true</c>. Sem ninguém reagindo à flag, o dono entraria no\r\n * painel com uma sessão que não abre nada. Quem reage é este serviço, ligado por\r\n * <c>provideOrechioAccount</c> — o produto não escreve código novo, só atualiza a versão.\r\n * </p>\r\n *\r\n * <p>\r\n * <b>Por que observar a sessão e não esperar o login chamar.</b> O login e o <c>GET /session</c>\r\n * (reload) terminam do mesmo jeito: <c>SessionStore.set</c>. Observar o store cobre os dois\r\n * sem tocar no <c>AuthClient</c> e sem pedir um callback à tela de login do produto, que o\r\n * pacote não possui. A navegação vem DEPOIS da que a tela de login faz ao sucesso e a\r\n * substitui — é a última que vale.\r\n * </p>\r\n *\r\n * <p>\r\n * A flag decide navegação, nunca autorização: a Platform só aceita o token de provisionamento\r\n * na rota de primeira filial.\r\n * </p>\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class ProvisioningRedirect {\r\n private readonly session = inject(SessionStore);\r\n private readonly router = inject(Router);\r\n private readonly injector = inject(Injector);\r\n\r\n private iniciado = false;\r\n\r\n start(): void {\r\n if (this.iniciado) return;\r\n this.iniciado = true;\r\n\r\n effect(\r\n () => {\r\n if (this.session.user()?.provisioningRequired !== true) return;\r\n untracked(() => void this.irParaAPrimeiraFilial());\r\n },\r\n { injector: this.injector },\r\n );\r\n }\r\n\r\n private async irParaAPrimeiraFilial(): Promise<void> {\r\n const destino = caminhoDaPrimeiraFilial(this.router.config);\r\n if (destino === null) {\r\n // Sem a rota montada o desvio é impossível, e calar deixaria o dono num painel que não\r\n // abre nada. Aviso, não exceção: lançar aqui derrubaria o login de todo mundo.\r\n console.warn(\r\n '[orechio-account] provisioningRequired recebido, mas ORECHIO_ACCOUNT_ROUTES não está ' +\r\n `montado de forma estática (rota \"${CAMINHO_RELATIVO}\" não encontrada em router.config).`,\r\n );\r\n return;\r\n }\r\n\r\n if (this.router.url.split(/[?#]/)[0] === destino) return;\r\n await this.router.navigateByUrl(destino, { replaceUrl: true });\r\n }\r\n}\r\n\r\n/**\r\n * Procura, em <c>router.config</c>, a rota de primeira filial e devolve o caminho ABSOLUTO dela.\r\n * O host monta <c>ORECHIO_ACCOUNT_ROUTES</c> onde quiser (<c>conta</c>, <c>auth</c>, a raiz); o\r\n * pacote só conhece o caminho relativo entre as telas, então o prefixo se descobre pela\r\n * árvore. Rotas carregadas sob demanda (<c>loadChildren</c>) não estão na árvore até serem\r\n * visitadas e não são procuradas.\r\n */\r\nexport function caminhoDaPrimeiraFilial(rotas: readonly Route[], prefixo = ''): string | null {\r\n for (const rota of rotas) {\r\n const caminho = [prefixo, rota.path ?? ''].filter((parte) => parte !== '').join('/');\r\n if (rota.path === CAMINHO_RELATIVO) return `/${caminho}`;\r\n if (rota.children) {\r\n const achada = caminhoDaPrimeiraFilial(rota.children, caminho);\r\n if (achada !== null) return achada;\r\n }\r\n }\r\n return null;\r\n}\r\n","import { ENVIRONMENT_INITIALIZER, InjectionToken, Provider, inject } from '@angular/core';\r\nimport { UrlTree } from '@angular/router';\r\nimport { ProvisioningRedirect } from './provisioning-redirect';\r\n\r\n/**\r\n * A fronteira do core compartilhado, como o browser a recebe (ADR-019 §3).\r\n *\r\n * <p>\r\n * Sem token nenhum, e isso é o contrato — não uma omissão. O BFF recebe os tokens da\r\n * Platform e os entrega ao mecanismo de sessão do host; o que chega aqui é o estado da\r\n * conta, nunca o material dela.\r\n * </p>\r\n */\r\nexport interface AccountCompletionResult {\r\n tenantId: string;\r\n branchId: string;\r\n applicationId: string;\r\n userId: string;\r\n /**\r\n * `OperationalReady` ou `ProductContinuation` — enum fechado do lado da Platform.\r\n * Trafega como string e é repassado **sem interpretação**: quem traduz isso em rota é o\r\n * host, nunca este pacote.\r\n */\r\n nextStep: string;\r\n}\r\n\r\n/** Rotas do BFF que o host montou. Defaults batem com `MapOrechioAccountEndpoints`. */\r\nexport interface OrechioAccountPaths {\r\n verifyEmail?: string;\r\n initialBranch?: string;\r\n forgotPassword?: string;\r\n resetPassword?: string;\r\n acceptInvite?: string;\r\n resendVerification?: string;\r\n}\r\n\r\nexport interface OrechioAccountConfig {\r\n /**\r\n * Prefixo do BFF de conta, same-origin. Nunca uma URL absoluta cross-origin: o modelo é\r\n * BFF no domínio do produto, e é isso que faz o cookie ser first-party.\r\n */\r\n basePath: string;\r\n\r\n paths: Required<OrechioAccountPaths>;\r\n\r\n /**\r\n * **O único ponto de extensão do pacote.**\r\n *\r\n * Chamado quando a conta fica operacional. O host decide para onde ir: um produto com\r\n * cadastro próprio manda para o passo dele, um produto sem passo próprio manda direto para\r\n * o painel. O pacote não conhece nenhuma dessas rotas, e é essa ignorância que o mantém\r\n * compartilhável (ADR-019 §3).\r\n *\r\n * Nem os nomes desses destinos aparecem aqui — nem em comentário. Um exemplo concreto de\r\n * um produto irmão é a primeira forma do acoplamento: vira referência, depois default,\r\n * depois `if`. A guarda de fronteira varre estes fontes e falha em termo vertical.\r\n */\r\n onCompleted: (result: AccountCompletionResult) => UrlTree | string;\r\n\r\n /**\r\n * Como o produto chama a primeira unidade operacional, no vocabulário dele. \"Unidade\" é o\r\n * default por ser o termo mais neutro que existe — cada produto tem o seu, e nenhum deles\r\n * é assunto do pacote. É por isso que isto é configuração e não literal na tela.\r\n */\r\n branchLabel: string;\r\n\r\n /** Para onde mandar quem termina um fluxo que não abre sessão (reset, convite aceito). */\r\n loginPath: string;\r\n}\r\n\r\nexport const ORECHIO_ACCOUNT_CONFIG = new InjectionToken<OrechioAccountConfig>(\r\n 'ORECHIO_ACCOUNT_CONFIG',\r\n);\r\n\r\nexport interface OrechioAccountOptions {\r\n basePath?: string;\r\n paths?: OrechioAccountPaths;\r\n onCompleted: OrechioAccountConfig['onCompleted'];\r\n branchLabel?: string;\r\n loginPath?: string;\r\n}\r\n\r\nexport function provideOrechioAccount(options: OrechioAccountOptions): Provider[] {\r\n if (typeof options?.onCompleted !== 'function') {\r\n // Falha na composição, não no primeiro cliente que termina o cadastro. Sem\r\n // `onCompleted` o pacote não teria para onde mandar ninguém, e o sintoma apareceria\r\n // exatamente no pior momento: com o usuário já cadastrado, olhando uma tela parada.\r\n throw new Error(\r\n 'provideOrechioAccount: `onCompleted` é obrigatório — é o host que decide a navegação.',\r\n );\r\n }\r\n\r\n const basePath = (options.basePath ?? '/api/account').replace(/\\/+$/, '');\r\n\r\n const config: OrechioAccountConfig = {\r\n basePath,\r\n paths: {\r\n verifyEmail: options.paths?.verifyEmail ?? '/verify-email',\r\n initialBranch: options.paths?.initialBranch ?? '/onboarding/branch',\r\n forgotPassword: options.paths?.forgotPassword ?? '/forgot-password',\r\n resetPassword: options.paths?.resetPassword ?? '/reset-password',\r\n acceptInvite: options.paths?.acceptInvite ?? '/accept-invite',\r\n resendVerification: options.paths?.resendVerification ?? '/resend-verification',\r\n },\r\n onCompleted: options.onCompleted,\r\n branchLabel: options.branchLabel ?? 'unidade',\r\n loginPath: options.loginPath ?? '/login',\r\n };\r\n\r\n return [\r\n { provide: ORECHIO_ACCOUNT_CONFIG, useValue: config },\r\n // PROV-1: liga o desvio da sessão de provisionamento para a tela de primeira filial. Fica\r\n // aqui, e não num guard ou callback do host, para o produto só precisar atualizar a versão.\r\n // `ENVIRONMENT_INITIALIZER` (e não `provideEnvironmentInitializer`) para o retorno continuar\r\n // `Provider[]`: um `EnvironmentProviders` aqui quebraria quem usa o resultado em\r\n // `providers` de componente ou o tipa como `Provider[]`.\r\n {\r\n provide: ENVIRONMENT_INITIALIZER,\r\n multi: true,\r\n useValue: () => inject(ProvisioningRedirect).start(),\r\n },\r\n ];\r\n}\r\n","import { Routes } from '@angular/router';\r\n\r\n/**\r\n * As rotas de conta, para o host registrar ONDE ELE QUISER.\r\n *\r\n * <p>\r\n * São `Routes[]` e não um módulo com prefixo fixo de propósito: o produto monta em\r\n * `/conta`, em `/auth`, na raiz, ou espalhadas — o pacote não reivindica caminho. O que ele\r\n * fixa são os caminhos RELATIVOS entre elas, porque o `verify-email` navega para\r\n * `../onboarding/branch` quando há provisioning pendente.\r\n * </p>\r\n *\r\n * <pre>\r\n * // no host:\r\n * { path: 'conta', children: ORECHIO_ACCOUNT_ROUTES }\r\n * </pre>\r\n *\r\n * <p>\r\n * Todas com `loadComponent`: quem só usa recuperação de senha não carrega a tela de\r\n * primeira filial, e o bundle inicial do produto não paga por telas que a maioria dos\r\n * usuários vê uma vez na vida.\r\n * </p>\r\n */\r\nexport const ORECHIO_ACCOUNT_ROUTES: Routes = [\r\n {\r\n path: 'verify-email',\r\n title: 'Confirmando e-mail',\r\n loadComponent: () => import('./pages/verify-email.page').then((m) => m.VerifyEmailPage),\r\n },\r\n {\r\n path: 'onboarding/branch',\r\n title: 'Primeira unidade',\r\n loadComponent: () => import('./pages/initial-branch.page').then((m) => m.InitialBranchPage),\r\n },\r\n {\r\n path: 'forgot-password',\r\n title: 'Recuperar acesso',\r\n loadComponent: () => import('./pages/forgot-password.page').then((m) => m.ForgotPasswordPage),\r\n },\r\n {\r\n path: 'reset-password',\r\n title: 'Nova senha',\r\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\r\n // `reset` também é o default do componente; declarado aqui para a rota dizer o que faz\r\n // sem exigir que se leia a implementação.\r\n data: { modo: 'reset' },\r\n },\r\n {\r\n path: 'accept-invite',\r\n title: 'Primeiro acesso',\r\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\r\n data: { modo: 'invite' },\r\n },\r\n {\r\n path: 'resend-verification',\r\n title: 'Reenviar confirmação',\r\n loadComponent: () =>\r\n import('./pages/resend-verification.page').then((m) => m.ResendVerificationPage),\r\n },\r\n];\r\n","/*\r\n * Public API Surface of @orechio/platform-angular/account\r\n *\r\n * ACCOUNT-1C — o entry de conta. Ele traz as TELAS do onboarding compartilhado; a sessão,\r\n * o login e os interceptors continuam no entry primário, onde já estavam (ADR-019 §5.1:\r\n * login/refresh/logout são KEEP-SEPARATE — Store e Portal resolvem sessão de formas\r\n * materialmente diferentes).\r\n *\r\n * O que NÃO é exportado é parte do contrato: `AccountClient` é detalhe de implementação, e\r\n * as páginas são alcançadas por `ORECHIO_ACCOUNT_ROUTES`, nunca importadas diretamente —\r\n * exportá-las convidaria o host a montar a própria árvore de rotas por cima delas, e os\r\n * caminhos relativos entre as telas deixariam de valer.\r\n */\r\n\r\nexport type {\r\n AccountCompletionResult,\r\n OrechioAccountConfig,\r\n OrechioAccountOptions,\r\n OrechioAccountPaths,\r\n} from './account-config';\r\nexport { ORECHIO_ACCOUNT_CONFIG, provideOrechioAccount } from './account-config';\r\n\r\nexport { ORECHIO_ACCOUNT_ROUTES } from './account.routes';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orechio/platform-angular",
3
- "version": "0.7.4",
3
+ "version": "0.8.1",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^22.1.0",
6
6
  "@angular/core": "^22.1.0",
@@ -64,6 +64,13 @@ interface SessionUser {
64
64
  roles: string[];
65
65
  name: string | null;
66
66
  email: string | null;
67
+ /**
68
+ * The session is the PROVISIONING one: the account has no branch yet and the only thing it can
69
+ * do is create the first (PROV-1). Optional and absent from hosts that do not send it. It
70
+ * steers navigation (`@orechio/platform-angular/account` redirects to the first-branch screen);
71
+ * it authorizes nothing.
72
+ */
73
+ provisioningRequired?: boolean;
67
74
  }
68
75
 
69
76
  /**
@@ -1 +0,0 @@
1
- {"version":3,"file":"orechio-platform-angular-account-initial-branch.page-C8a1eU3L.mjs","sources":["../../../projects/platform-angular/account/src/pages/initial-branch.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { Router } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { AccountClient } from '../account-client';\r\nimport { ORECHIO_ACCOUNT_CONFIG } from '../account-config';\r\n\r\n@Component({\r\n selector: 'orechio-account-initial-branch',\r\n standalone: true,\r\n imports: [FormsModule],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-account\">\r\n <ng-content select=\"[orechioAccountBrand]\"></ng-content>\r\n\r\n <h1>Configure sua primeira {{ rotulo }}</h1>\r\n <p class=\"orechio-account__muted\">\r\n É onde seus lançamentos vão nascer. Dá para criar outras depois.\r\n </p>\r\n\r\n <form (ngSubmit)=\"enviar()\">\r\n <label for=\"orechio-account-branch-name\">Nome da {{ rotulo }}</label>\r\n <input\r\n id=\"orechio-account-branch-name\"\r\n name=\"nome\"\r\n type=\"text\"\r\n autocomplete=\"organization\"\r\n [(ngModel)]=\"nome\"\r\n [disabled]=\"enviando()\"\r\n required\r\n />\r\n\r\n @if (erro()) {\r\n <p class=\"orechio-account__error\">{{ mensagemDeErro() }}</p>\r\n }\r\n\r\n <button type=\"submit\" [disabled]=\"enviando() || !nome.trim()\">\r\n {{ enviando() ? 'Criando…' : 'Concluir' }}\r\n </button>\r\n </form>\r\n </section>\r\n `,\r\n styles: `\r\n .orechio-account {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--orechio-space-3, 0.75rem);\r\n max-width: 28rem;\r\n margin: 0 auto;\r\n padding: var(--orechio-space-6, 1.5rem);\r\n color: var(--orechio-color-text, inherit);\r\n font-family: var(--orechio-font-body, inherit);\r\n }\r\n .orechio-account form { display: flex; flex-direction: column; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-account__muted { color: var(--orechio-color-text-muted, #666); }\r\n .orechio-account__error { color: var(--orechio-color-danger, #b00020); }\r\n `,\r\n})\r\nexport class InitialBranchPage {\r\n private readonly router = inject(Router);\r\n private readonly client = inject(AccountClient);\r\n private readonly config = inject(ORECHIO_ACCOUNT_CONFIG);\r\n\r\n protected readonly rotulo = this.config.branchLabel;\r\n protected nome = '';\r\n protected readonly enviando = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n protected mensagemDeErro(): string {\r\n const code = this.erro()?.code;\r\n if (code === 'onboarding.branch_already_exists') return `Esta ${this.rotulo} já foi criada.`;\r\n if (code === 'session.missing') return 'Sua sessão expirou. Abra o link do e-mail novamente.';\r\n if (code === 'onboarding.not_in_provisioning') return 'Este passo já foi concluído.';\r\n if (code === 'rate_limit_exceeded') return 'Muitas tentativas. Aguarde alguns minutos.';\r\n if (code?.startsWith('branch.')) return 'Escolha um nome válido.';\r\n return 'Não foi possível concluir. Tente novamente.';\r\n }\r\n\r\n protected enviar(): void {\r\n const nome = this.nome.trim();\r\n if (!nome || this.enviando()) return;\r\n\r\n this.enviando.set(true);\r\n this.erro.set(null);\r\n\r\n this.client.createInitialBranch(nome).subscribe({\r\n next: async (resultado) => {\r\n // O pacote entrega o resultado e sai. Quem decide para onde ir é o host —\r\n // `nextStep` é repassado inteiro, sem interpretação (ADR-019 §3).\r\n const destino = this.config.onCompleted(resultado);\r\n await this.router.navigateByUrl(destino, { replaceUrl: true });\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.erro.set(problema);\r\n this.enviando.set(false);\r\n },\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}