@softize/opus 19.0.0 → 20.1.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.
- package/CHANGELOG.md +239 -0
- package/bin/cli.mjs +5 -0
- package/bin/lib/check.mjs +198 -10
- package/bin/lib/copy.mjs +29 -0
- package/bin/lib/mcp.mjs +1 -1
- package/docs/adr/0020-icon-tiles-are-explicitly-composed.md +8 -0
- package/docs/adr/0021-content-carries-structure-and-semantic-element.md +7 -0
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +19 -1
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -0
- package/docs/adr/0025-content-frame-treatment-does-not-change-semantics.md +134 -0
- package/docs/data-products.md +15 -9
- package/docs/protocol.md +3 -4
- package/docs/releasing.md +2 -2
- package/package.json +2 -2
- package/registry/skills/create-opus-action/scripts/scaffold.mjs +2 -1
- package/registry/templates/app/package.json +1 -1
- package/src/core/runtime.ts +96 -8
- package/src/core/types.ts +5 -2
- package/src/ui/components/patterns/action-form-card.tsx +11 -1
- package/src/ui/components/patterns/content-header.tsx +109 -18
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/list.tsx +91 -92
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/presentation.tsx +52 -37
- package/src/ui/components/patterns/split.tsx +44 -4
- package/src/ui/components/patterns/surface-anatomy.ts +35 -8
- package/src/ui/components/patterns/surface-header.tsx +58 -25
- package/src/ui/components/patterns/trigger.tsx +12 -2
- package/src/ui/components/primitives/alert.tsx +9 -1
- package/src/ui/components/primitives/avatar.tsx +7 -3
- package/src/ui/components/primitives/card.tsx +1 -1
- package/src/ui/components/primitives/carousel.tsx +34 -5
- package/src/ui/components/primitives/checkbox.tsx +11 -3
- package/src/ui/components/primitives/command.tsx +2 -1
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/dialog.tsx +42 -11
- package/src/ui/components/primitives/drawer.tsx +10 -2
- package/src/ui/components/primitives/empty.tsx +1 -1
- package/src/ui/components/primitives/icon-picker.tsx +6 -2
- package/src/ui/components/primitives/icon-tile.tsx +7 -0
- package/src/ui/components/primitives/input-group.tsx +9 -2
- package/src/ui/components/primitives/input-otp.tsx +1 -1
- package/src/ui/components/primitives/kbd.tsx +1 -1
- package/src/ui/components/primitives/label.tsx +3 -0
- package/src/ui/components/primitives/menu.tsx +5 -0
- package/src/ui/components/primitives/popover.tsx +1 -1
- package/src/ui/components/primitives/progress.tsx +9 -1
- package/src/ui/components/primitives/slider.tsx +62 -11
- package/src/ui/components/primitives/truncate.tsx +10 -1
- package/src/ui/docs/content/accordion.md +74 -27
- package/src/ui/docs/content/action-form-card.md +24 -15
- package/src/ui/docs/content/action-form-dialog.md +17 -7
- package/src/ui/docs/content/action-form.md +92 -77
- package/src/ui/docs/content/action-list-dialog.md +75 -38
- package/src/ui/docs/content/action-list.md +141 -58
- package/src/ui/docs/content/action-trigger.md +27 -10
- package/src/ui/docs/content/action-view.md +14 -14
- package/src/ui/docs/content/actions.md +161 -66
- package/src/ui/docs/content/ai.md +43 -44
- package/src/ui/docs/content/alert.md +83 -29
- package/src/ui/docs/content/ask.md +51 -26
- package/src/ui/docs/content/aspect-ratio.md +22 -24
- package/src/ui/docs/content/audit.md +23 -19
- package/src/ui/docs/content/auth.md +38 -19
- package/src/ui/docs/content/avatar.md +54 -5
- package/src/ui/docs/content/badge.md +29 -9
- package/src/ui/docs/content/breadcrumb.md +21 -8
- package/src/ui/docs/content/button.md +73 -43
- package/src/ui/docs/content/calendar.md +134 -26
- package/src/ui/docs/content/card.md +41 -15
- package/src/ui/docs/content/carousel.md +37 -6
- package/src/ui/docs/content/chat.md +118 -37
- package/src/ui/docs/content/checkbox.md +73 -7
- package/src/ui/docs/content/cli.md +94 -55
- package/src/ui/docs/content/collapsible.md +43 -19
- package/src/ui/docs/content/command.md +30 -8
- package/src/ui/docs/content/composer.md +23 -8
- package/src/ui/docs/content/content.md +119 -27
- package/src/ui/docs/content/copyable.md +39 -8
- package/src/ui/docs/content/customization.md +55 -46
- package/src/ui/docs/content/cycle.md +33 -18
- package/src/ui/docs/content/data-products.md +115 -0
- package/src/ui/docs/content/data-state.md +20 -6
- package/src/ui/docs/content/data.md +20 -19
- package/src/ui/docs/content/detail.md +12 -5
- package/src/ui/docs/content/dialog.md +107 -24
- package/src/ui/docs/content/dictionary-value.md +4 -4
- package/src/ui/docs/content/dock.md +54 -19
- package/src/ui/docs/content/dot.md +35 -5
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/empty-value.md +20 -6
- package/src/ui/docs/content/empty.md +24 -3
- package/src/ui/docs/content/events.md +76 -16
- package/src/ui/docs/content/executions.md +9 -9
- package/src/ui/docs/content/field.md +21 -6
- package/src/ui/docs/content/getting-started.md +44 -34
- package/src/ui/docs/content/icon-picker.md +19 -13
- package/src/ui/docs/content/icon-tile.md +38 -15
- package/src/ui/docs/content/input-otp.md +58 -7
- package/src/ui/docs/content/input.md +22 -3
- package/src/ui/docs/content/item.md +53 -6
- package/src/ui/docs/content/kbd.md +5 -1
- package/src/ui/docs/content/label.md +13 -4
- package/src/ui/docs/content/log.md +5 -5
- package/src/ui/docs/content/markdown.md +5 -4
- package/src/ui/docs/content/mcp.md +49 -23
- package/src/ui/docs/content/menu.md +95 -10
- package/src/ui/docs/content/metric-card.md +12 -4
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +118 -12
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +55 -5
- package/src/ui/docs/content/popover.md +42 -5
- package/src/ui/docs/content/presentation.md +91 -47
- package/src/ui/docs/content/progress.md +27 -14
- package/src/ui/docs/content/queue.md +39 -21
- package/src/ui/docs/content/radio-group.md +26 -4
- package/src/ui/docs/content/router.md +29 -10
- package/src/ui/docs/content/runtime.md +90 -54
- package/src/ui/docs/content/scheduler.md +53 -21
- package/src/ui/docs/content/scroll-area.md +42 -35
- package/src/ui/docs/content/select.md +78 -33
- package/src/ui/docs/content/semantic-context.md +21 -2
- package/src/ui/docs/content/separator.md +15 -9
- package/src/ui/docs/content/sidebar.md +3 -2
- package/src/ui/docs/content/skeleton.md +6 -1
- package/src/ui/docs/content/slider.md +37 -9
- package/src/ui/docs/content/spinner.md +4 -3
- package/src/ui/docs/content/split.md +19 -0
- package/src/ui/docs/content/storage.md +8 -9
- package/src/ui/docs/content/switch.md +10 -7
- package/src/ui/docs/content/table.md +46 -8
- package/src/ui/docs/content/tabs.md +14 -4
- package/src/ui/docs/content/testing.md +76 -31
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +48 -7
- package/src/ui/docs/content/toggle.md +3 -3
- package/src/ui/docs/content/tokens.md +15 -26
- package/src/ui/docs/content/tooltip.md +18 -2
- package/src/ui/docs/content/truncate.md +36 -6
- package/src/ui/docs/content/ui.md +74 -22
- package/src/ui/docs/content/upgrading.md +45 -36
- package/src/ui/docs/plugin.ts +36 -2
- package/src/ui/docs/registry.tsx +6 -0
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +32 -32
- package/src/ui/react.tsx +1 -0
- package/src/vite/design.ts +16 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
agrupadas e sempre alcançáveis.
|
|
1
|
+
Use `Dock` para as ferramentas de uma superfície de trabalho, como um canvas ou um editor. Essas
|
|
2
|
+
telas raramente comportam mais uma barra no topo: a navegação da aplicação já ocupa esse lugar, e
|
|
3
|
+
um segundo cabeçalho empilharia duas faixas com a mesma função. A Dock coloca as ações sobre a
|
|
4
|
+
própria superfície, agrupadas e sempre alcançáveis.
|
|
5
5
|
|
|
6
6
|
```tsx preview
|
|
7
7
|
<div className="relative h-40 w-full rounded-lg border border-border bg-muted/20">
|
|
@@ -23,8 +23,8 @@ agrupadas e sempre alcançáveis.
|
|
|
23
23
|
|
|
24
24
|
O contêiner precisa ser `relative` — a Dock se ancora nele, não na janela. `position` escolhe a
|
|
25
25
|
aresta: `bottom` (centro), `bottom-left` e `bottom-right`. Uma superfície pode ter mais de uma
|
|
26
|
-
barra quando os papéis são distintos, como ferramentas ao centro e controles de
|
|
27
|
-
|
|
26
|
+
barra quando os papéis são distintos, como ferramentas ao centro e controles de visualização à
|
|
27
|
+
direita.
|
|
28
28
|
|
|
29
29
|
## Grupos e divisórias
|
|
30
30
|
|
|
@@ -44,10 +44,43 @@ Mensagem de sucesso é transitória por natureza — some depois de confirmar
|
|
|
44
44
|
permanece até o estado mudar. Essa decisão é da aplicação, que conhece o ciclo; o componente
|
|
45
45
|
apenas reserva o lugar.
|
|
46
46
|
|
|
47
|
-
`actions` recebe o que o recurso aberto permite fazer — em geral o mesmo menu que a
|
|
48
|
-
tem na navegação. Assim
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
`actions` recebe o que o recurso aberto permite fazer — em geral o mesmo [`Menu`](/ui/menu) que a
|
|
48
|
+
linha dele tem na navegação. Assim, renomear ou excluir também alcançam o item que está na tela.
|
|
49
|
+
Apenas o texto é região viva: anunciar o menu a cada mudança de estado seria ruído para quem usa
|
|
50
|
+
leitor de tela.
|
|
51
|
+
|
|
52
|
+
No exemplo, o estado de falha fica no canto superior esquerdo com o menu do recurso, e os controles
|
|
53
|
+
de visualização ficam no canto inferior direito.
|
|
54
|
+
|
|
55
|
+
```tsx preview
|
|
56
|
+
<div className="relative h-40 w-full rounded-lg border border-border bg-muted/20">
|
|
57
|
+
<SurfaceStatus
|
|
58
|
+
position="top-left"
|
|
59
|
+
context="danger"
|
|
60
|
+
actions={
|
|
61
|
+
<Menu>
|
|
62
|
+
<MenuTrigger asChild>
|
|
63
|
+
<Button variant="ghost" size="icon" aria-label="Ações do fluxo"><MoreHorizontal /></Button>
|
|
64
|
+
</MenuTrigger>
|
|
65
|
+
<MenuContent align="start">
|
|
66
|
+
<MenuItem><Pencil /> Renomear</MenuItem>
|
|
67
|
+
<MenuSeparator />
|
|
68
|
+
<MenuItem context="danger"><Trash2 /> Excluir</MenuItem>
|
|
69
|
+
</MenuContent>
|
|
70
|
+
</Menu>
|
|
71
|
+
}
|
|
72
|
+
>
|
|
73
|
+
Falha ao salvar
|
|
74
|
+
</SurfaceStatus>
|
|
75
|
+
<Dock label="Controles de visualização" position="bottom-right">
|
|
76
|
+
<DockGroup>
|
|
77
|
+
<DockAction icon={<ZoomOut />} label="Afastar" onClick={() => {}} />
|
|
78
|
+
<DockAction icon={<ZoomIn />} label="Aproximar" onClick={() => {}} />
|
|
79
|
+
<DockAction icon={<Maximize />} label="Ajustar à tela" onClick={() => {}} />
|
|
80
|
+
</DockGroup>
|
|
81
|
+
</Dock>
|
|
82
|
+
</div>
|
|
83
|
+
```
|
|
51
84
|
|
|
52
85
|
## Modo e execução
|
|
53
86
|
|
|
@@ -57,36 +90,36 @@ o rótulo, que também é o nome acessível.
|
|
|
57
90
|
|
|
58
91
|
## Teclado
|
|
59
92
|
|
|
60
|
-
A barra é uma `toolbar`: as setas andam entre as ações e Home/End vão às pontas. Cada ação
|
|
61
|
-
|
|
62
|
-
já monta.
|
|
93
|
+
A barra é uma `toolbar`: as setas andam entre as ações e Home/End vão às pontas. Cada ação exibe o nome em um
|
|
94
|
+
[tooltip](/ui/tooltip), então `<TooltipProvider>` precisa existir na raiz do app — o projeto criado
|
|
95
|
+
por `opus create` já o monta.
|
|
63
96
|
|
|
64
97
|
## Propriedades de Dock
|
|
65
98
|
|
|
66
99
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
67
100
|
| --- | --- | --- | --- |
|
|
68
101
|
| `position` | `'bottom' \| 'bottom-left' \| 'bottom-right'` | `'bottom'` | Aresta do contêiner onde a barra se ancora. |
|
|
69
|
-
| `label` | `string` | | Nome acessível da barra. |
|
|
70
|
-
| `children` | `ReactNode` | | Os grupos de ações, em `DockGroup`. |
|
|
102
|
+
| `label` | `string` | obrigatório | Nome acessível da barra. |
|
|
103
|
+
| `children` | `ReactNode` | obrigatório | Os grupos de ações, em `DockGroup`. |
|
|
71
104
|
| `className` | `string` | | Classes adicionais da barra. |
|
|
72
105
|
|
|
73
106
|
## Propriedades de DockGroup
|
|
74
107
|
|
|
75
108
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
76
109
|
| --- | --- | --- | --- |
|
|
77
|
-
| `children` | `ReactNode` | | As ações do grupo, em `DockAction`. A divisória antes do grupo é aplicada automaticamente, exceto no primeiro. |
|
|
110
|
+
| `children` | `ReactNode` | obrigatório | As ações do grupo, em `DockAction`. A divisória antes do grupo é aplicada automaticamente, exceto no primeiro. |
|
|
78
111
|
| `className` | `string` | | Classes adicionais do grupo. |
|
|
79
112
|
|
|
80
113
|
## Propriedades de DockAction
|
|
81
114
|
|
|
82
115
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
83
116
|
| --- | --- | --- | --- |
|
|
84
|
-
| `icon` | `ReactNode` | | Ícone exibido no botão. |
|
|
85
|
-
| `label` | `string` | | Nome da ação; vira o nome acessível e o tooltip quando não há `hint`. |
|
|
117
|
+
| `icon` | `ReactNode` | obrigatório | Ícone exibido no botão. |
|
|
118
|
+
| `label` | `string` | obrigatório | Nome da ação; vira o nome acessível e o tooltip quando não há `hint`. |
|
|
86
119
|
| `pressed` | `boolean` | | Presente quando a ação liga um modo: aplica o preenchimento e expõe `aria-pressed`. Ausente quando a ação apenas executa. |
|
|
87
120
|
| `disabled` | `boolean` | `false` | Desabilita a ação e a tira da navegação por setas. |
|
|
88
121
|
| `hint` | `ReactNode` | `label` | Conteúdo do tooltip quando ele precisa dizer mais que o rótulo. |
|
|
89
|
-
| `onClick` | `() => void` | | Executa a ação ou alterna o modo. |
|
|
122
|
+
| `onClick` | `() => void` | obrigatório | Executa a ação ou alterna o modo. |
|
|
90
123
|
| `className` | `string` | | Classes adicionais do botão. |
|
|
91
124
|
|
|
92
125
|
## Propriedades de SurfaceStatus
|
|
@@ -96,3 +129,5 @@ já monta.
|
|
|
96
129
|
| `position` | `'top-right' \| 'top-left'` | `'top-right'` | Canto da superfície onde o estado se ancora. |
|
|
97
130
|
| `context` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O que o estado comunica; tinge borda e texto pela família semântica. |
|
|
98
131
|
| `actions` | `ReactNode` | | Ações do recurso aberto, fora da região viva. |
|
|
132
|
+
| `children` | `ReactNode` | | O texto do estado, anunciado pela região viva a cada mudança. |
|
|
133
|
+
| `className` | `string` | | Classes adicionais do contêiner. |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Indicador visual compacto para estados que já têm contexto. O tamanho permanece fixo; `context`
|
|
2
|
-
seleciona o significado e `variant` escolhe ponto sólido ou contornado. Quando a cor reforça
|
|
3
|
-
acessível. Sem `label`, o ponto é decorativo
|
|
2
|
+
seleciona o significado e `variant` escolhe ponto sólido ou contornado. Quando a cor reforça
|
|
3
|
+
significado, `label` fornece o nome acessível. Sem `label`, o ponto é decorativo e fica oculto dos
|
|
4
|
+
leitores de tela.
|
|
4
5
|
|
|
5
6
|
```tsx preview
|
|
6
7
|
<div className="flex items-center gap-4">
|
|
@@ -8,11 +9,25 @@ acessível. Sem `label`, o ponto é decorativo.
|
|
|
8
9
|
<Dot context="warning" label="Atenção" />
|
|
9
10
|
<Dot context="danger" label="Falhou" />
|
|
10
11
|
<Dot context="info" label="Publicado" />
|
|
11
|
-
<Dot context="neutral"
|
|
12
|
+
<Dot context="neutral" />
|
|
12
13
|
</div>
|
|
13
14
|
```
|
|
14
15
|
|
|
15
|
-
Use `Badge` quando o estado precisar permanecer legível sem depender do contexto ao
|
|
16
|
+
Use [`Badge`](/ui/badge) quando o estado precisar permanecer legível sem depender do contexto ao
|
|
17
|
+
redor.
|
|
18
|
+
|
|
19
|
+
## Ponto contornado
|
|
20
|
+
|
|
21
|
+
`variant="outline"` desenha só o contorno. Ele diferencia dois estados que compartilham o mesmo
|
|
22
|
+
`context`, como uma execução agendada e uma em andamento. O `label` continua nomeando cada estado.
|
|
23
|
+
|
|
24
|
+
```tsx preview
|
|
25
|
+
<div className="flex items-center gap-4 text-sm">
|
|
26
|
+
<span className="flex items-center gap-2"><Dot context="success" label="Em execução" /> developer</span>
|
|
27
|
+
<span className="flex items-center gap-2"><Dot context="success" variant="outline" label="Agendado" /> reviewer</span>
|
|
28
|
+
<span className="flex items-center gap-2"><Dot context="neutral" variant="outline" label="Inativo" /> designer</span>
|
|
29
|
+
</div>
|
|
30
|
+
```
|
|
16
31
|
|
|
17
32
|
## Propriedades de Dot
|
|
18
33
|
|
|
@@ -20,4 +35,19 @@ Use `Badge` quando o estado precisar permanecer legível sem depender do context
|
|
|
20
35
|
|---|---|---|---|
|
|
21
36
|
| `context` | `'neutral' \| 'primary' \| 'info' \| 'success' \| 'warning' \| 'danger'` | `'neutral'` | O significado da cor. |
|
|
22
37
|
| `variant` | `'solid' \| 'outline'` | `'solid'` | Ponto preenchido ou só contornado. |
|
|
23
|
-
| `label` | `string` | | Nome acessível quando a cor comunica estado;
|
|
38
|
+
| `label` | `string` | | Nome acessível quando a cor comunica estado; o ponto passa a ser exposto como imagem com esse nome. Sem ele, o ponto é decorativo (`aria-hidden`). |
|
|
39
|
+
|
|
40
|
+
Os demais atributos vão para o elemento `span`.
|
|
41
|
+
|
|
42
|
+
## Função dotVariants
|
|
43
|
+
|
|
44
|
+
`dotVariants({ context, variant })` retorna as classes do ponto para um elemento que não é
|
|
45
|
+
renderizado pelo `Dot`. Os padrões são os mesmos do componente: `neutral` e `solid`. Sem o
|
|
46
|
+
componente, o elemento não recebe `label` nem `aria-hidden`; trate a acessibilidade nele.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
function dotVariants(options?: {
|
|
50
|
+
context?: DotContext | null
|
|
51
|
+
variant?: DotVariant | null
|
|
52
|
+
}): string
|
|
53
|
+
```
|
|
@@ -8,15 +8,15 @@ de fechamento.
|
|
|
8
8
|
```tsx preview
|
|
9
9
|
<Drawer>
|
|
10
10
|
<DrawerTrigger asChild>
|
|
11
|
-
<Button variant="outline">Ver
|
|
11
|
+
<Button variant="outline">Ver veículo</Button>
|
|
12
12
|
</DrawerTrigger>
|
|
13
13
|
<DrawerContent side="right">
|
|
14
14
|
<DrawerHeader>
|
|
15
|
-
<DrawerTitle>
|
|
15
|
+
<DrawerTitle>ABC-1D23</DrawerTitle>
|
|
16
16
|
</DrawerHeader>
|
|
17
|
-
<DrawerBody className="text-sm text-muted-foreground">
|
|
18
|
-
<p>
|
|
19
|
-
<p
|
|
17
|
+
<DrawerBody className="space-y-1 text-sm text-muted-foreground">
|
|
18
|
+
<p>Corolla XEi 2022 · 38.400 km</p>
|
|
19
|
+
<p>Última revisão em 12 de maio.</p>
|
|
20
20
|
</DrawerBody>
|
|
21
21
|
</DrawerContent>
|
|
22
22
|
</Drawer>
|
|
@@ -36,11 +36,11 @@ neutra e sutil; `closeSize` escolhe entre `xs` e `sm`, com `sm` como padrão.
|
|
|
36
36
|
</DrawerTrigger>
|
|
37
37
|
<DrawerContent side="left">
|
|
38
38
|
<DrawerHeader>
|
|
39
|
-
<DrawerTitle>Editar
|
|
39
|
+
<DrawerTitle>Editar veículo</DrawerTitle>
|
|
40
40
|
</DrawerHeader>
|
|
41
41
|
<DrawerBody className="space-y-2">
|
|
42
|
-
<Label htmlFor="
|
|
43
|
-
<Input id="
|
|
42
|
+
<Label htmlFor="vehicle-owner">Responsável</Label>
|
|
43
|
+
<Input id="vehicle-owner" defaultValue="Marina Alves" />
|
|
44
44
|
</DrawerBody>
|
|
45
45
|
<DrawerFooter>
|
|
46
46
|
<ButtonGroup mode="spaced">
|
|
@@ -75,11 +75,11 @@ ruído.
|
|
|
75
75
|
</DrawerTrigger>
|
|
76
76
|
<DrawerContent layout="continuous">
|
|
77
77
|
<DrawerHeader>
|
|
78
|
-
<DrawerTitle>Resumo da
|
|
78
|
+
<DrawerTitle>Resumo da revisão</DrawerTitle>
|
|
79
79
|
</DrawerHeader>
|
|
80
80
|
<DrawerBody>
|
|
81
81
|
<p className="text-sm text-muted-foreground">
|
|
82
|
-
Concluída em 2
|
|
82
|
+
Concluída em 2 horas, sem pendências registradas.
|
|
83
83
|
</p>
|
|
84
84
|
</DrawerBody>
|
|
85
85
|
</DrawerContent>
|
|
@@ -98,7 +98,7 @@ título ocupa a folga, as ações vão para o extremo oposto e o botão de fecha
|
|
|
98
98
|
nó que não seja um desses slots interrompe a renderização com um erro; contexto e conteúdo
|
|
99
99
|
pertencem ao `DrawerBody`.
|
|
100
100
|
|
|
101
|
-
`DrawerMedia` é a moldura de ícone
|
|
101
|
+
`DrawerMedia` é a moldura de ícone do Opus — o mesmo [`IconTile`](/ui/icon-tile) —, útil para
|
|
102
102
|
identificar o tipo do recurso aberto. `DrawerNavigation` carrega o retorno de um fluxo encadeado,
|
|
103
103
|
em que o painel permanece aberto e troca de etapa.
|
|
104
104
|
|
|
@@ -110,7 +110,7 @@ em que o painel permanece aberto e troca de etapa.
|
|
|
110
110
|
<DrawerContent side="right">
|
|
111
111
|
<DrawerHeader>
|
|
112
112
|
<DrawerNavigation>
|
|
113
|
-
<Button variant="ghost" size="icon
|
|
113
|
+
<Button variant="ghost" size="icon" aria-label="Voltar para a lista">
|
|
114
114
|
<ArrowLeft />
|
|
115
115
|
</Button>
|
|
116
116
|
</DrawerNavigation>
|
|
@@ -119,7 +119,7 @@ em que o painel permanece aberto e troca de etapa.
|
|
|
119
119
|
</DrawerMedia>
|
|
120
120
|
<DrawerTitle>ABC-1D23</DrawerTitle>
|
|
121
121
|
<DrawerActions>
|
|
122
|
-
<Button variant="ghost" size="icon
|
|
122
|
+
<Button variant="ghost" size="icon" aria-label="Mais ações">
|
|
123
123
|
<Ellipsis />
|
|
124
124
|
</Button>
|
|
125
125
|
</DrawerActions>
|
|
@@ -131,15 +131,57 @@ em que o painel permanece aberto e troca de etapa.
|
|
|
131
131
|
</Drawer>
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
Voltar e ações do cabeçalho usam `Button` `ghost` no tamanho `icon` (2.25rem), o mesmo que a
|
|
135
|
+
[`Presentation`](/ui/presentation) usa no voltar. O botão de fechar é menor: um `CloseButton` no
|
|
136
|
+
quadrado `icon-sm` (1.75rem) por padrão.
|
|
137
|
+
|
|
134
138
|
O cabeçalho não recebe descrição: o painel já nomeia o recurso, e o contexto que ajuda a decidir
|
|
135
139
|
aparece no começo do corpo, como texto ou `Alert`.
|
|
136
140
|
|
|
141
|
+
## Impedir o fechamento durante uma operação
|
|
142
|
+
|
|
143
|
+
Esc, o clique no overlay e o botão de fechar encerram o painel. Enquanto uma operação não puder ser
|
|
144
|
+
interrompida, passe `closeDisabled` a `DrawerContent`: o botão de fechar continua visível, mas
|
|
145
|
+
desabilitado, e Esc e o clique no overlay deixam de fechar o painel. Se você também passar
|
|
146
|
+
`onEscapeKeyDown`, `onPointerDownOutside` ou `onInteractOutside`, esses handlers continuam sendo
|
|
147
|
+
chamados antes do bloqueio.
|
|
148
|
+
|
|
149
|
+
`closeDisabled` não alcança as ações que você compõe no painel, como um `Cancelar` com
|
|
150
|
+
`DrawerClose`; desabilite-as pelo mesmo estado da operação. Quando a pessoa precisar responder antes
|
|
151
|
+
de continuar, use [`Dialog mode="alert"`](/ui/dialog), que não fecha pelo clique externo.
|
|
152
|
+
|
|
153
|
+
## Acessibilidade
|
|
154
|
+
|
|
155
|
+
- `DrawerTitle` nomeia o painel para tecnologias assistivas. Quando um texto conciso do corpo também
|
|
156
|
+
precisar descrevê-lo, relacione seu `id` por `aria-describedby` em `DrawerContent`.
|
|
157
|
+
- Com `modal` ativo, o padrão, o foco permanece dentro do painel e o restante da página fica
|
|
158
|
+
inacessível até o fechamento.
|
|
159
|
+
- Ao fechar, o foco retorna ao gatilho.
|
|
160
|
+
- O botão de fechar tem o nome acessível `Fechar`. Botões só com ícone no cabeçalho precisam de
|
|
161
|
+
`aria-label`.
|
|
162
|
+
|
|
137
163
|
## Propriedades de Drawer
|
|
138
164
|
|
|
139
|
-
| Propriedade | Tipo | Padrão
|
|
140
|
-
| -------------- | ------------------------- |
|
|
141
|
-
| `open` | `boolean` |
|
|
142
|
-
| `
|
|
165
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
166
|
+
| -------------- | ------------------------- | ------- | ---------------------------------------------------------------------------- |
|
|
167
|
+
| `open` | `boolean` | | Estado no modo controlado. |
|
|
168
|
+
| `defaultOpen` | `boolean` | `false` | Abertura inicial no modo não controlado. |
|
|
169
|
+
| `onOpenChange` | `(open: boolean) => void` | | Recebe as mudanças de abertura, inclusive Esc, overlay e botão de fechar. |
|
|
170
|
+
| `modal` | `boolean` | `true` | Mantém foco e interação restritos ao painel aberto e exibe o overlay. |
|
|
171
|
+
|
|
172
|
+
## Propriedades de DrawerTrigger
|
|
173
|
+
|
|
174
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
175
|
+
| ----------- | ----------- | ------- | -------------------------------------------------- |
|
|
176
|
+
| `asChild` | `boolean` | `false` | Usa o filho como gatilho sem criar outro elemento. |
|
|
177
|
+
| `children` | `ReactNode` | | Controle que abre o painel. |
|
|
178
|
+
|
|
179
|
+
## Propriedades de DrawerClose
|
|
180
|
+
|
|
181
|
+
| Propriedade | Tipo | Padrão | Descrição |
|
|
182
|
+
| ----------- | ----------- | ------- | ----------------------------------------------------------- |
|
|
183
|
+
| `asChild` | `boolean` | `false` | Aplica o fechamento ao filho; use com `Button`. |
|
|
184
|
+
| `children` | `ReactNode` | | Controle que fecha o painel, como o `Cancelar` do rodapé. |
|
|
143
185
|
|
|
144
186
|
## Propriedades de DrawerContent
|
|
145
187
|
|
|
@@ -147,9 +189,11 @@ aparece no começo do corpo, como texto ou `Alert`.
|
|
|
147
189
|
| ----------------- | ---------------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
|
|
148
190
|
| `side` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'right'` | Borda de onde o painel aparece. |
|
|
149
191
|
| `layout` | `'continuous' \| 'sectioned'` | `'sectioned'` | Anatomia da superfície inteira; os slots leem esse valor e não o recebem. |
|
|
150
|
-
| `showCloseButton` | `boolean` | `true` | Inclui
|
|
151
|
-
| `closeDisabled` | `boolean` | `false` | Mantém
|
|
192
|
+
| `showCloseButton` | `boolean` | `true` | Inclui o botão de fechar no cabeçalho. Sem ele, Esc e o overlay continuam fechando o painel. |
|
|
193
|
+
| `closeDisabled` | `boolean` | `false` | Mantém o botão de fechar visível e desabilitado e impede que Esc e o clique no overlay fechem o painel. |
|
|
152
194
|
| `closeSize` | `"xs" \| "sm"` | `"sm"` | Define o tamanho da ação circular de fechar. |
|
|
195
|
+
| `className` | `string` | | Ajusta o painel, como a largura. |
|
|
196
|
+
| `children` | `ReactNode` | | Cabeçalho, corpo e rodapé. |
|
|
153
197
|
|
|
154
198
|
## Propriedades de DrawerHeader
|
|
155
199
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Valor ausente tem uma representação padrão. Nas células de tabela e nos pares de
|
|
2
|
-
aparece o travessão, enquanto a leitura assistiva recebe “Não
|
|
3
|
-
`ActionList` e o `DetailField` já fazem isso sozinhos;
|
|
4
|
-
|
|
1
|
+
Valor ausente tem uma representação padrão. Nas células de tabela e nos pares de
|
|
2
|
+
[`DetailField`](/ui/detail) aparece o travessão, enquanto a leitura assistiva recebe “Não
|
|
3
|
+
informado”. As colunas de [`ActionList`](/ui/action-list) e o `DetailField` já fazem isso sozinhos;
|
|
4
|
+
uma célula ou um componente próprio usa `EmptyValue` em vez de repetir a condicional.
|
|
5
5
|
|
|
6
6
|
```tsx preview
|
|
7
7
|
<div className="flex items-center gap-6 text-sm">
|
|
@@ -15,8 +15,19 @@ primitiva em vez de repetir a condicional.
|
|
|
15
15
|
## O que conta como ausência
|
|
16
16
|
|
|
17
17
|
`null`, `undefined`, string vazia e string só com espaços. `0`, `false` e coleções vazias são
|
|
18
|
-
valores legítimos: quem apresenta booleanos e listas decide a própria forma. `isAbsentValue`
|
|
19
|
-
|
|
18
|
+
valores legítimos: quem apresenta booleanos e listas decide a própria forma. `isAbsentValue` aplica
|
|
19
|
+
a mesma regra em células e componentes próprios.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
function isAbsentValue(value: unknown): boolean
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { EmptyValue, isAbsentValue } from '@softize/opus/ui/react'
|
|
27
|
+
|
|
28
|
+
// Em uma célula própria:
|
|
29
|
+
isAbsentValue(row.phone) ? <EmptyValue compact /> : formatPhone(row.phone)
|
|
30
|
+
```
|
|
20
31
|
|
|
21
32
|
## O domínio dá o significado
|
|
22
33
|
|
|
@@ -46,3 +57,6 @@ columns: [
|
|
|
46
57
|
|---|---|---|---|
|
|
47
58
|
| `label` | `string` | `'Não informado'` | O que a ausência significa neste domínio. |
|
|
48
59
|
| `compact` | `boolean` | `false` | Célula compacta: travessão visível, rótulo só para leitura assistiva. |
|
|
60
|
+
|
|
61
|
+
Os demais atributos, como `className`, vão para o elemento `span`. `EmptyValue` não aceita
|
|
62
|
+
`children`: o texto entra por `label`.
|
|
@@ -4,9 +4,9 @@ Use `Empty` quando a própria região está disponível para receber ou criar co
|
|
|
4
4
|
tracejada diferencia esse espaço de uma estrutura de dados já estabelecida e pode combinar título,
|
|
5
5
|
descrição, mídia e ação.
|
|
6
6
|
|
|
7
|
-
Para uma lista ou tabela carregada, mas sem registros, use `DataState` ou
|
|
8
|
-
sólida preserva a estrutura. Se a região já estiver
|
|
9
|
-
no pai para evitar uma moldura dupla.
|
|
7
|
+
Para uma lista ou tabela carregada, mas sem registros, use [`DataState`](/ui/data-state) ou
|
|
8
|
+
[`ActionList`](/ui/action-list): a moldura sólida preserva a estrutura. Se a região já estiver
|
|
9
|
+
emoldurada pelo pai, mantenha a borda somente no pai para evitar uma moldura dupla.
|
|
10
10
|
|
|
11
11
|
## Com ícone
|
|
12
12
|
|
|
@@ -78,3 +78,24 @@ ou mídias que tenham presença visual própria.
|
|
|
78
78
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
79
79
|
|---|---|---|---|
|
|
80
80
|
| `variant` | `'default' \| 'icon'` | `'default'` | `icon` aplica uma moldura quadrada e muted; `default` exibe a mídia sem fundo. |
|
|
81
|
+
|
|
82
|
+
## Propriedades de EmptyHeader
|
|
83
|
+
|
|
84
|
+
`EmptyHeader` agrupa mídia, título e descrição no centro, com largura limitada para a leitura. Não
|
|
85
|
+
tem props próprias; aceita as de um elemento `div`.
|
|
86
|
+
|
|
87
|
+
## Propriedades de EmptyTitle
|
|
88
|
+
|
|
89
|
+
`EmptyTitle` exibe a situação em destaque. Ele renderiza um `div`, sem nível de heading; quando a
|
|
90
|
+
região precisar de um título na estrutura da página, passe `role="heading"` e `aria-level`. Não tem
|
|
91
|
+
props próprias; aceita as de um elemento `div`.
|
|
92
|
+
|
|
93
|
+
## Propriedades de EmptyDescription
|
|
94
|
+
|
|
95
|
+
`EmptyDescription` apresenta a explicação em cor atenuada e sublinha os links filhos diretos. Não
|
|
96
|
+
tem props próprias; aceita as de um elemento `div`.
|
|
97
|
+
|
|
98
|
+
## Propriedades de EmptyActions
|
|
99
|
+
|
|
100
|
+
`EmptyActions` empilha e centraliza as ações abaixo do cabeçalho. Não tem props próprias; aceita as
|
|
101
|
+
de um elemento `div`.
|
|
@@ -4,9 +4,9 @@ title: Eventos
|
|
|
4
4
|
|
|
5
5
|
# Eventos
|
|
6
6
|
|
|
7
|
-
Use eventos quando uma action precisar anunciar um fato sem conhecer
|
|
8
|
-
`ctx.emit` publica o evento; reactions e integrações
|
|
9
|
-
|
|
7
|
+
Use eventos quando uma action precisar anunciar um fato sem conhecer quem vai reagir a ele.
|
|
8
|
+
`ctx.emit` publica o evento; reactions e integrações o recebem pelo `EventBusAdapter`. O formato do
|
|
9
|
+
evento é o mesmo do handler que emite até quem consome.
|
|
10
10
|
|
|
11
11
|
## O contrato
|
|
12
12
|
|
|
@@ -24,6 +24,8 @@ interface DomainEvent<T = unknown> {
|
|
|
24
24
|
tenant?: string | null
|
|
25
25
|
data: T
|
|
26
26
|
source: { action: string; actionId: string; correlation?: string }
|
|
27
|
+
trace?: TraceContext // trace da execução que emitiu
|
|
28
|
+
meta?: Record<string, unknown> // o terceiro argumento de ctx.emit
|
|
27
29
|
}
|
|
28
30
|
```
|
|
29
31
|
|
|
@@ -32,10 +34,15 @@ interface DomainEvent<T = unknown> {
|
|
|
32
34
|
```ts
|
|
33
35
|
import { mittEvents } from '@softize/opus/events/mitt'
|
|
34
36
|
|
|
35
|
-
const bus = mittEvents()
|
|
37
|
+
const bus = mittEvents() // no mesmo processo do runtime
|
|
36
38
|
```
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
O `mitt` entrega pelo nome exato do evento ou por `*`, que recebe todos. Padrões parciais, como
|
|
41
|
+
`nota.*`, não são reconhecidos: assine `*` e filtre no handler.
|
|
42
|
+
|
|
43
|
+
## Emitir de uma action
|
|
44
|
+
|
|
45
|
+
Configure o barramento no runtime e declare no binding os eventos que a action emite:
|
|
39
46
|
|
|
40
47
|
```ts
|
|
41
48
|
const runtime = createRuntime({
|
|
@@ -43,19 +50,72 @@ const runtime = createRuntime({
|
|
|
43
50
|
eventBus: bus,
|
|
44
51
|
})
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
53
|
+
export const notaEmitir = bindAction(notaEmitirContract, {
|
|
54
|
+
emits: ['nota.emitida'],
|
|
55
|
+
handler: async (ctx, input) => {
|
|
56
|
+
const nota = await repo.create(input)
|
|
57
|
+
// O runtime preenche id, timestamp, actor, tenant, source e trace.
|
|
58
|
+
await ctx.emit('nota.emitida', { id: nota.id, valor: nota.valor })
|
|
59
|
+
return nota
|
|
60
|
+
},
|
|
61
|
+
})
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
`emits` pertence ao binding, como `handler`, e o runtime só o considera em actions `simple` e
|
|
65
|
+
`form`. Quando uma delas declara `emits` e o handler emite um evento fora da lista, o runtime
|
|
66
|
+
registra um aviso no log. Esse aviso vem de `config.warnUndeclaredEmits`, ativo por padrão fora de produção. Sem `emits`
|
|
67
|
+
declarado, nenhum aviso é gerado.
|
|
68
|
+
|
|
69
|
+
Em uma action `list` ou `view`, o tipo do binding aceita `emits`, mas a lista não entra no manifest
|
|
70
|
+
nem na conferência dos eventos emitidos. Para que a declaração não pareça ter efeito, o runtime
|
|
71
|
+
registra o aviso `runtime.action_field_ignored` quando a action é registrada com `config.dev` ativo,
|
|
72
|
+
o padrão sempre que `config.env` não é `'production'`. O [`opus check`](/docs/cli) aponta o mesmo
|
|
73
|
+
caso com a regra `ignored-by-kind`. Se a operação anuncia um fato, declare-a como `simple` ou `form`.
|
|
74
|
+
|
|
75
|
+
`config.emitMode` decide o que acontece quando a publicação não é possível. No padrão, `lenient`, a
|
|
76
|
+
falta de `EventBusAdapter` e uma falha de `publish` viram aviso no log e o handler segue. Em
|
|
77
|
+
`strict`, as duas situações interrompem a action com `emit.no_bus` ou `emit.failed`.
|
|
78
|
+
|
|
79
|
+
## Reagir a um evento
|
|
80
|
+
|
|
81
|
+
Uma reaction executa código quando um evento é publicado. Como nas actions, a declaração pode ficar
|
|
82
|
+
no código compartilhado e o efeito, no servidor:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { bindReaction, defineReactionContract } from '@softize/opus/core'
|
|
86
|
+
|
|
87
|
+
// shared/ — qual evento e o que a reação significa.
|
|
88
|
+
export const notaAvisarFinanceiro = defineReactionContract<{ id: string; valor: number }>({
|
|
89
|
+
name: 'nota.avisar-financeiro',
|
|
90
|
+
on: 'nota.emitida',
|
|
91
|
+
description: 'Avisa o financeiro quando uma nota é emitida.',
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// api/ — o efeito.
|
|
95
|
+
export const notaAvisarFinanceiroImpl = bindReaction(notaAvisarFinanceiro, {
|
|
96
|
+
handler: async (ctx, event) => {
|
|
97
|
+
ctx.log.info('nota emitida', { id: event.data.id, valor: event.data.valor })
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Registre a reaction com as actions, em `defineDomain({ reactions: [notaAvisarFinanceiroImpl] })`
|
|
103
|
+
ou em `runtime.register`. Com reactions registradas, o `start()` exige um `EventBusAdapter` e falha
|
|
104
|
+
com `runtime.eventbus_required` quando ele não existe. `defineReaction` declara tudo num objeto só,
|
|
105
|
+
quando não há separação entre cliente e servidor.
|
|
106
|
+
|
|
107
|
+
A reaction roda com provenance `reaction`; `ctx.user` traz somente o `id` do autor do evento, e
|
|
108
|
+
`ctx.tenantId`, o tenant do evento. O contexto não tem `can`: a decisão fica no `authorize(ctx, event)` opcional, e uma resposta `false` descarta o evento
|
|
109
|
+
sem erro. Uma falha no handler é registrada no log e não chega à action que emitiu.
|
|
57
110
|
|
|
58
111
|
## Limites atuais
|
|
59
112
|
|
|
60
|
-
O driver `mitt`
|
|
61
|
-
|
|
113
|
+
O driver `mitt` funciona no mesmo processo, sem durabilidade nem entrega entre processos.
|
|
114
|
+
|
|
115
|
+
Uma reaction não encadeia outra por evento: dentro dela, `ctx.emit` descarta o evento sem publicá-lo.
|
|
116
|
+
Com `config.dev` ativo, cada descarte registra o aviso `runtime.reaction_emit_ignored` no log, com o
|
|
117
|
+
nome da reaction e do evento; em produção, o descarte continua silencioso. Quando outro consumidor
|
|
118
|
+
precisa desse fato, emita o evento no handler da action que o produz e declare-o em `emits`.
|
|
119
|
+
|
|
120
|
+
`retry`, `timeout`, `dedup` e `concurrency` são aceitos na declaração, mas o runtime ainda não os
|
|
121
|
+
aplica: uma reaction que falha não é repetida.
|
|
@@ -9,7 +9,7 @@ O módulo é experimental e opt-in: instalar o Opus não passa a descobrir crons
|
|
|
9
9
|
O executor continua dono do estado; este recurso mantém uma fotografia para consulta.
|
|
10
10
|
|
|
11
11
|
Para colocar trabalho em segundo plano, use [Filas](/docs/queue). Para acompanhar trabalho já
|
|
12
|
-
existente, adapte seu executor à projeção abaixo. Não
|
|
12
|
+
existente, adapte seu executor à projeção abaixo. Não use a projeção para decidir reenvio ou retry.
|
|
13
13
|
|
|
14
14
|
## Publicar uma fotografia
|
|
15
15
|
|
|
@@ -43,8 +43,8 @@ Cron que enfileira trabalho continua com
|
|
|
43
43
|
`trigger: 'schedule'`; entrar na fila não cria outra execução. Retomada mantém o ID e avança
|
|
44
44
|
revisão/tentativa conforme o executor. A projeção não valida transições de negócio.
|
|
45
45
|
|
|
46
|
-
Uma falha de projeção lança erro para o adaptador tratar.
|
|
47
|
-
depois a partir do executor; não
|
|
46
|
+
Uma falha de projeção lança erro para o adaptador tratar. Registre essa falha e reconcilie
|
|
47
|
+
depois a partir do executor; não deixe o erro provocar retry de um efeito já realizado.
|
|
48
48
|
`observe` não contém proteção automática para o fluxo de negócio do consumidor.
|
|
49
49
|
|
|
50
50
|
## Persistência e processos separados
|
|
@@ -60,7 +60,7 @@ durável; o aviso não é histórico nem fila de entrega.
|
|
|
60
60
|
|
|
61
61
|
`pgExecutionChanges({ connect, onError, channel?, reconnectMs? })` compartilha uma conexão
|
|
62
62
|
LISTEN entre seus assinantes. `connect` deve entregar uma conexão nova, já conectada, dedicada
|
|
63
|
-
e com timeout de conexão configurado. Não
|
|
63
|
+
e com timeout de conexão configurado. Não forneça uma conexão emprestada por um pooler
|
|
64
64
|
transacional. O driver fecha a conexão quando sai o último assinante, registra falhas por
|
|
65
65
|
`onError` e tenta reconectar após `reconnectMs` (padrão: 1.000 ms; mínimo: 100 ms).
|
|
66
66
|
|
|
@@ -87,15 +87,15 @@ async function openProgress(
|
|
|
87
87
|
}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
Para `authorize(null)`,
|
|
90
|
+
Para `authorize(null)`, verifique a sessão e o direito de acompanhar. Para um aviso, verifique
|
|
91
91
|
também tenant, operação e recurso exato. Essa verificação ocorre novamente durante o stream,
|
|
92
92
|
inclusive nos heartbeats e antes de emitir uma invalidação. Heartbeats revalidam a sessão
|
|
93
93
|
independentemente da fila de avisos: a função pode receber um aviso e `null` concorrentemente.
|
|
94
94
|
As actions de leitura devem aplicar a mesma política: filtrar a
|
|
95
95
|
consulta por tenant não substitui autorização.
|
|
96
96
|
|
|
97
|
-
O cliente recebe somente `event: invalidate` com `{}`. Ao recebê-lo,
|
|
98
|
-
actions autorizadas.
|
|
97
|
+
O cliente recebe somente `event: invalidate` com `{}`. Ao recebê-lo, invalide as queries das
|
|
98
|
+
actions autorizadas. Mantenha uma conexão compartilhada no provider da aplicação, não uma por
|
|
99
99
|
card. A primeira invalidação e a reconexão exigem nova fotografia; não há `Last-Event-ID` nem
|
|
100
100
|
replay. O navegador pode usar `EventSource` com cookies same-origin e sua reconexão nativa.
|
|
101
101
|
|
|
@@ -105,9 +105,9 @@ de 128 avisos pendentes e 16 frames na saída; excedê-los encerra a conexão pa
|
|
|
105
105
|
sem acumular um log em memória. Sessão é reavaliada no máximo a cada heartbeat enquanto a
|
|
106
106
|
autorização responde; configure timeout no serviço de autorização para não mantê-la pendente.
|
|
107
107
|
|
|
108
|
-
IDs, tenant e operação nos avisos são metadados operacionais internos. Não
|
|
108
|
+
IDs, tenant e operação nos avisos são metadados operacionais internos. Não use CPF, telefone,
|
|
109
109
|
e-mail ou segredo nesses identificadores. PostgreSQL NOTIFY não é uma fronteira de autorização:
|
|
110
|
-
|
|
110
|
+
restrinja as credenciais e o acesso ao banco. Nada do aviso é copiado para o SSE.
|
|
111
111
|
|
|
112
112
|
## Detectar ausência de sinal
|
|
113
113
|
|