@softize/opus 18.5.0 → 20.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.
- package/CHANGELOG.md +275 -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/0023-surfaces-share-one-anatomy-and-two-layouts.md +298 -0
- package/docs/adr/0024-fill-height-propagates-by-context.md +118 -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 +16 -12
- package/src/ui/components/patterns/fill-region.ts +150 -0
- package/src/ui/components/patterns/heading-level.ts +16 -0
- package/src/ui/components/patterns/list.tsx +103 -73
- package/src/ui/components/patterns/page.tsx +20 -4
- package/src/ui/components/patterns/pane.tsx +294 -0
- package/src/ui/components/patterns/presentation.tsx +81 -54
- package/src/ui/components/patterns/sidebar.tsx +13 -72
- package/src/ui/components/patterns/split.tsx +51 -38
- package/src/ui/components/patterns/surface-anatomy.ts +246 -0
- package/src/ui/components/patterns/surface-header.tsx +51 -6
- package/src/ui/components/patterns/surface-media.tsx +69 -0
- 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 +91 -29
- 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 +14 -5
- package/src/ui/components/primitives/copyable.tsx +120 -10
- package/src/ui/components/primitives/detail.tsx +10 -13
- package/src/ui/components/primitives/dialog.tsx +179 -45
- package/src/ui/components/primitives/drawer.tsx +131 -24
- 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/metric-card.tsx +98 -39
- 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 +134 -52
- 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 +252 -35
- 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 +37 -7
- 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 +219 -28
- 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 +193 -22
- 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 +28 -5
- 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 +68 -21
- package/src/ui/docs/content/observability.md +10 -9
- package/src/ui/docs/content/page.md +125 -11
- package/src/ui/docs/content/pagination.md +13 -5
- package/src/ui/docs/content/pane.md +300 -0
- 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 +25 -45
- package/src/ui/docs/content/skeleton.md +13 -4
- 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 +34 -20
- 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 +14 -2
- package/src/ui/drivers/react.tsx +16 -8
- package/src/ui/meta.ts +38 -32
- package/src/ui/react.tsx +14 -0
- package/src/vite/design.ts +16 -4
|
@@ -1,36 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Evolução contínua
|
|
3
|
+
---
|
|
4
|
+
|
|
1
5
|
# Evolução contínua
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
Use este fluxo quando o seu projeto precisar de algo que o Opus ainda não oferece ou encontrar um
|
|
8
|
+
defeito nele. O Opus incorpora um comportamento quando ele se repete em mais de um projeto; então o
|
|
9
|
+
projeto troca a implementação local pelo import. Preferência de estilo e conveniência isolada não
|
|
10
|
+
são motivo para incorporar uma peça.
|
|
4
11
|
|
|
5
|
-
##
|
|
12
|
+
## Registrar um apontamento
|
|
6
13
|
|
|
7
|
-
Cada apontamento vira uma Issue
|
|
14
|
+
Cada apontamento vira uma Issue em
|
|
15
|
+
[github.com/softize-dev/opus](https://github.com/softize-dev/opus/issues). Antes de abrir uma nova,
|
|
16
|
+
procure uma Issue sobre o mesmo comportamento e comente nela. Há dois tipos:
|
|
8
17
|
|
|
9
|
-
- **`enhancement
|
|
10
|
-
|
|
18
|
+
- **`enhancement`:** código genérico que serviria a qualquer projeto. Antes de apontar, confira se o
|
|
19
|
+
Opus já oferece o recurso no inventário de componentes, nos exports ou nesta documentação; se
|
|
20
|
+
oferecer, use-o.
|
|
21
|
+
- **`bug`:** defeito ou limitação do próprio Opus, encontrado durante o uso.
|
|
11
22
|
|
|
12
|
-
Descreva cada apontamento com estes campos
|
|
23
|
+
Descreva cada apontamento com estes campos, como nos exemplos:
|
|
13
24
|
|
|
14
25
|
```text
|
|
15
26
|
{"type": "enhancement", "behavior": "o comportamento em uma frase.", "file": "src/onde-vive.ts", "kind": "ui|sdk|driver|infra"}
|
|
16
|
-
{"type": "bug", "behavior": "o que quebra ou falta.", "file": "onde se manifesta.", "note": "
|
|
27
|
+
{"type": "bug", "behavior": "o que quebra ou falta.", "file": "onde se manifesta.", "note": "como reproduzir e o contorno aplicado."}
|
|
17
28
|
```
|
|
18
29
|
|
|
19
|
-
|
|
30
|
+
O pacote não coleta apontamentos do seu projeto automaticamente.
|
|
20
31
|
|
|
21
|
-
|
|
32
|
+
## Seguir sem esperar a correção
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
2. **Entregue** a feature com o workaround.
|
|
25
|
-
3. **Aponte** na Issue e siga em frente.
|
|
34
|
+
Uma lacuna ou um defeito do Opus não precisa bloquear a entrega:
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
1. **Contorne no projeto:** componha um wrapper local. O Opus é distribuído como código-fonte, então
|
|
37
|
+
qualquer superfície dele pode ser envolvida. Não edite `node_modules`, porque a alteração se
|
|
38
|
+
perde na próxima instalação.
|
|
39
|
+
2. **Entregue** a funcionalidade com o contorno.
|
|
40
|
+
3. **Registre** o apontamento na Issue.
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
A correção no Opus acontece em paralelo. Enquanto ela não sai, o contorno continua no projeto.
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
## Como o apontamento termina
|
|
32
45
|
|
|
33
|
-
- **
|
|
34
|
-
|
|
46
|
+
- Um **enhancement** aceito é implementado no Opus e publicado em uma nova versão. Ao
|
|
47
|
+
[atualizar o Opus](/docs/upgrading), o projeto troca o contorno pelo import.
|
|
48
|
+
- Um **bug** vira uma correção com entrada no `CHANGELOG`; na atualização, o contorno sai.
|
|
35
49
|
|
|
36
|
-
A
|
|
50
|
+
A decisão sobre o que entra no Opus fica com a Softize, que mantém o pacote. O registro das peças
|
|
51
|
+
incorporadas está no `PROMOTED.md` do pacote.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Produtos de Dados
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Produtos de Dados
|
|
6
|
+
|
|
7
|
+
Use um Produto de Dados quando um conjunto de dados precisar de identidade estável para catálogos,
|
|
8
|
+
agentes e revisão, independentemente da action que o entrega. A declaração diz o que o conjunto
|
|
9
|
+
representa, de onde vem, quem responde por ele e quais actions o disponibilizam. Ela não é uma
|
|
10
|
+
consulta, uma tabela nem uma view, e não executa nada: cada action continua sendo a fronteira de
|
|
11
|
+
execução, recorte e autorização.
|
|
12
|
+
|
|
13
|
+
## Declarar e registrar
|
|
14
|
+
|
|
15
|
+
`defineDataProduct` declara o produto e valida seus campos no momento em que roda. O domínio o
|
|
16
|
+
registra em `dataProducts`, ao lado das actions e entidades citadas:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { defineDataProduct, defineDomain } from '@softize/opus/core'
|
|
20
|
+
import { LeadEntity } from './entity.ts' // defineEntity({ name: 'lead', … })
|
|
21
|
+
import { leadList } from './actions/list.ts' // action 'lead.list'
|
|
22
|
+
import { leadFunnel } from './actions/funnel.ts' // action 'lead.funnel'
|
|
23
|
+
|
|
24
|
+
export const salesLeads = defineDataProduct({
|
|
25
|
+
id: 'sales.leads',
|
|
26
|
+
version: 1,
|
|
27
|
+
label: 'Leads comerciais',
|
|
28
|
+
description: 'Leads e seus resultados comerciais.',
|
|
29
|
+
owner: 'Vendas',
|
|
30
|
+
grain: 'Um lead.',
|
|
31
|
+
classification: 'internal',
|
|
32
|
+
nature: 'real',
|
|
33
|
+
sources: [{ id: 'crm', label: 'CRM comercial' }],
|
|
34
|
+
entities: ['lead'],
|
|
35
|
+
access: {
|
|
36
|
+
permissionContexts: ['sales'],
|
|
37
|
+
organizationalScopes: ['unit', 'team'],
|
|
38
|
+
},
|
|
39
|
+
interfaces: ['lead.list', 'lead.funnel'],
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const leadDomain = defineDomain({
|
|
43
|
+
name: 'lead',
|
|
44
|
+
entities: { LeadEntity },
|
|
45
|
+
actions: { leadList, leadFunnel },
|
|
46
|
+
dataProducts: { salesLeads },
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`interfaces` e `entities` usam os nomes declarados em `bindAction`/`defineContract` e em
|
|
51
|
+
`defineEntity`, não nomes de arquivo ou de tabela. `sources` identifica os sistemas ou conjuntos de
|
|
52
|
+
origem; uma fonte não precisa ser uma entidade do Opus.
|
|
53
|
+
|
|
54
|
+
## Verificações
|
|
55
|
+
|
|
56
|
+
A declaração é conferida em três momentos:
|
|
57
|
+
|
|
58
|
+
- `defineDataProduct` lança `TypeError` quando o `id` não tem namespace (como `sales.leads`), a
|
|
59
|
+
`version` não é um inteiro positivo, falta texto obrigatório, `sources` ou `interfaces` estão
|
|
60
|
+
vazias ou há itens repetidos.
|
|
61
|
+
- `defineDomain` recusa um produto duplicado no domínio e referências a actions ou entidades que o
|
|
62
|
+
domínio não declara (`domain.data_product_interface_not_found` e
|
|
63
|
+
`domain.data_product_entity_not_found`).
|
|
64
|
+
- `opus check` lê o código sem executá-lo e confere exportação, `id`, `version`, duplicidade e as
|
|
65
|
+
referências a actions e entidades do projeto, com as regras de prefixo `data-product`. Para isso,
|
|
66
|
+
o produto precisa ser `export const` e usar valores literais.
|
|
67
|
+
|
|
68
|
+
## Acesso e segurança
|
|
69
|
+
|
|
70
|
+
`access` documenta o alcance esperado de forma verificável. Ele ajuda o catálogo e a revisão a
|
|
71
|
+
perceber, por exemplo, que uma interface deveria respeitar unidade e equipe. `access` não é RLS nem
|
|
72
|
+
autorização automática: cada action listada em `interfaces` precisa aplicar o próprio `authorize` e
|
|
73
|
+
os recortes no handler ou no repositório, inclusive quando chamada por IA ou por
|
|
74
|
+
[MCP](/docs/mcp).
|
|
75
|
+
|
|
76
|
+
`permissionContexts` nomeia chaves do vocabulário de permissão do projeto. Ele não descreve área,
|
|
77
|
+
departamento nem contexto de tela; um projeto que não usa essa dimensão declara uma lista vazia.
|
|
78
|
+
O nome antigo `contexts` ainda é aceito e projetado para compatibilidade, mas declarações novas usam
|
|
79
|
+
`permissionContexts`.
|
|
80
|
+
|
|
81
|
+
## Onde o produto aparece
|
|
82
|
+
|
|
83
|
+
- `opus gen` publica os produtos no `.opus/manifest.json`, e `opus introspect` os inclui no modelo
|
|
84
|
+
do projeto. Veja [CLI opus](/docs/cli).
|
|
85
|
+
- Em `runtime.aiTools()`, cada action exposta à IA traz em `metadata.dataProducts` os IDs dos
|
|
86
|
+
produtos relacionados e, em `metadata.dataProductLabels`, o `label` de cada ID. Veja
|
|
87
|
+
[IA generativa](/docs/ai).
|
|
88
|
+
- No servidor MCP, as mesmas informações aparecem em `_meta['com.softize.opus/data-products']` e
|
|
89
|
+
`_meta['com.softize.opus/data-product-labels']`.
|
|
90
|
+
|
|
91
|
+
Esses metadados servem a quem hospeda o agente: mostre o `label` às pessoas e use o ID na execução e
|
|
92
|
+
na auditoria. Eles não entram no prompt e não concedem acesso. Nenhuma dessas projeções é a fonte
|
|
93
|
+
da verdade: para mudar um produto, corrija a declaração e gere de novo.
|
|
94
|
+
|
|
95
|
+
## Evoluir um produto
|
|
96
|
+
|
|
97
|
+
Incremente `version` quando a forma observável do produto mudar. Para substituí-lo, mantenha a
|
|
98
|
+
declaração anterior com `status: 'deprecated'` e `replacedBy` apontando para o novo ID durante a
|
|
99
|
+
migração. Um produto ativo não declara `replacedBy`.
|
|
100
|
+
|
|
101
|
+
## Referência de DataProductConfig
|
|
102
|
+
|
|
103
|
+
| Campo | Contrato |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `id` | Identidade estável com namespace, como `sales.leads`. |
|
|
106
|
+
| `version` | Inteiro positivo; muda com a forma observável do produto. |
|
|
107
|
+
| `label`, `description` | Nome e descrição para pessoas. |
|
|
108
|
+
| `owner` | Responsável técnico ou de negócio. |
|
|
109
|
+
| `grain` | O que uma linha ou observação representa. |
|
|
110
|
+
| `classification`, `nature` | Classificação (como `internal` ou `personal`) e natureza (como `real`, `demo` ou `synthetic`) definidas pelo projeto. |
|
|
111
|
+
| `sources` | Pelo menos uma fonte `{ id, label, description? }`, sem `id` repetido. |
|
|
112
|
+
| `entities` | Nomes das entidades Opus que estruturam o produto; pode ser vazia. |
|
|
113
|
+
| `access` | `permissionContexts` e `organizationalScopes`, ambos listas de texto. |
|
|
114
|
+
| `interfaces` | Nomes das actions que disponibilizam o produto; pelo menos uma. |
|
|
115
|
+
| `status`, `replacedBy` | `active` (padrão) ou `deprecated`; um produto `deprecated` exige `replacedBy`. |
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
## Estados
|
|
2
2
|
|
|
3
|
-
Use `DataState` para apresentar carregamento, erro, vazio e conteúdo de uma mesma consulta
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Use `DataState` para apresentar carregamento, erro, vazio e conteúdo de uma mesma consulta, na escala
|
|
4
|
+
de uma seção. Para o estado da página inteira, use `PageState`, descrito em [`Page`](/ui/page); para
|
|
5
|
+
uma coleção carregada por uma action `kind: 'list'`, [`ActionList`](/ui/action-list) já trata esses
|
|
6
|
+
estados.
|
|
7
|
+
|
|
8
|
+
Cada estado tem uma apresentação fixa:
|
|
9
|
+
|
|
10
|
+
- **Carregando:** um contêiner `role="status"` com o [`Spinner`](/ui/spinner) centralizado. Quando o
|
|
11
|
+
formato do conteúdo já é conhecido, em vez de passar `loading`, renderize o
|
|
12
|
+
[`Skeleton`](/ui/skeleton) durante a espera: ele reproduz esse formato e evita o salto de layout.
|
|
13
|
+
- **Vazio:** um [`Empty`](/ui/empty) com moldura sólida e `emptyMessage` como frase discreta.
|
|
14
|
+
- **Erro:** um [`Alert`](/ui/alert) de contexto `danger` com a mensagem segura de `errorMessage`,
|
|
15
|
+
sem expor o texto técnico de `error`. Com `onRetry`, o aviso ganha uma ação `ghost` só com ícone,
|
|
16
|
+
e `retryLabel` fornece o nome acessível e o tooltip dessa ação.
|
|
17
|
+
|
|
18
|
+
Quando mais de uma condição vale ao mesmo tempo, vence a primeira desta ordem: erro, carregando,
|
|
19
|
+
vazio. O conteúdo aparece somente quando nenhuma delas vale. Assim, uma nova tentativa com
|
|
20
|
+
`loading` e `error` ativos continua mostrando o erro até que `error` volte a `null`.
|
|
21
|
+
|
|
22
|
+
Para uma ação em andamento depois do clique, use `busy` em [`Button`](/ui/button).
|
|
9
23
|
|
|
10
24
|
```tsx preview col
|
|
11
25
|
<div className="w-full space-y-3">
|
|
@@ -32,9 +32,9 @@ export const SkillEntity = defineEntity({
|
|
|
32
32
|
|
|
33
33
|
## Código em camelCase, banco em snake_case
|
|
34
34
|
|
|
35
|
-
O schema tipado do Kysely e todas as consultas usam camelCase
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
O schema tipado do Kysely e todas as consultas usam camelCase (`workspaceId`, `ghRepo`). O banco
|
|
36
|
+
usa snake_case, definido na DDL do schema idempotente, como `db/schema.sql`. `CamelCasePlugin`
|
|
37
|
+
converte os nomes nas consultas e nos resultados. Um sink que não usa o Kysely com o
|
|
38
38
|
plugin, como `audit_log`, mantém os nomes do banco.
|
|
39
39
|
|
|
40
40
|
```ts
|
|
@@ -52,19 +52,20 @@ await db.selectFrom('agents').select(['isDefault', 'roleId']).where('workspaceId
|
|
|
52
52
|
|
|
53
53
|
Cada etapa possui um papel diferente:
|
|
54
54
|
|
|
55
|
-
- `opus db migrate`
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- `opus seed`
|
|
59
|
-
|
|
55
|
+
- `opus db migrate` aplica o schema idempotente (`config.schema`), um único script SQL em que
|
|
56
|
+
`IF NOT EXISTS` e condições de guarda cobrem tanto o banco novo quanto a atualização. Em seguida,
|
|
57
|
+
compara entidades e banco e encerra com exit ≠ 0 quando há divergência.
|
|
58
|
+
- `opus seed` aplica datasets de desenvolvimento e teste declarados com `defineSeed` e registrados no
|
|
59
|
+
`opus.config.ts`. Ele só conecta com `NODE_ENV=development` ou `test`. Veja
|
|
60
|
+
[CLI opus](/docs/cli).
|
|
60
61
|
|
|
61
62
|
## Campos JSON
|
|
62
63
|
|
|
63
|
-
`kyselyRepo` serializa campos `t.json()` na escrita (`insert` e `update`), guiado pela
|
|
64
|
-
declaração. Sem
|
|
65
|
-
array do
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
`kyselyRepo` serializa os campos `t.json()` na escrita (`insert` e `update`), guiado pela
|
|
65
|
+
declaração da entidade. Sem essa etapa, o driver `pg` converte um objeto simples, mas transforma um
|
|
66
|
+
array em literal de array do Postgres, o que é inválido para `jsonb` e não aparece no typecheck.
|
|
67
|
+
Uma string passa sem alteração, o que preserva valores já serializados; na leitura, o `pg` devolve
|
|
68
|
+
o objeto. Uma consulta escrita fora do repositório continua responsável pela própria serialização.
|
|
68
69
|
|
|
69
70
|
## SQL gerado por modelo
|
|
70
71
|
|
|
@@ -79,13 +80,13 @@ const bi = readOnlyContextPool({ pool: new Pool({ connectionString, max: 4 }) })
|
|
|
79
80
|
const { rows } = await bi.query(sqlDoLlm, { role: roleFor(ctx) })
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
1. **Pool com limite** (`max`
|
|
83
|
+
1. **Pool com limite** (`max`, `maxConcurrent` ou ambos): impede que as consultas consumam todas as
|
|
83
84
|
conexões do aplicativo.
|
|
84
|
-
2. **`BEGIN TRANSACTION READ ONLY
|
|
85
|
-
3. **`SET LOCAL ROLE` por transação
|
|
86
|
-
|
|
87
|
-
4. **Protocolo estendido
|
|
88
|
-
(`SELECT 1; DROP …`)
|
|
85
|
+
2. **`BEGIN TRANSACTION READ ONLY`:** o servidor rejeita tentativas de escrita.
|
|
86
|
+
3. **`SET LOCAL ROLE` por transação:** o contexto define o alcance da consulta. Crie os roles nas
|
|
87
|
+
suas migrações com grants fechados por padrão; sem isso, `sales_read` leria `hr_employees`.
|
|
88
|
+
4. **Protocolo estendido:** o SQL sempre recebe os valores separadamente, e múltiplas instruções
|
|
89
|
+
(`SELECT 1; DROP …`) são recusadas pelo próprio protocolo.
|
|
89
90
|
|
|
90
91
|
Ao terminar, `DISCARD ALL` limpa a conexão. Se a limpeza falhar, a conexão é descartada em vez de
|
|
91
92
|
voltar ao pool. `statement_timeout`, aplicado por transação, limita consultas demoradas; o padrão é
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Use `DetailGroup` para organizar dados somente leitura como pares de rótulo e valor.
|
|
2
2
|
`DetailField` representa cada par e aceita conteúdo React em `label` e `value`. Para entrada e
|
|
3
|
-
validação, use `Field
|
|
3
|
+
validação, use [`Field`](/ui/field).
|
|
4
4
|
|
|
5
5
|
```tsx preview col
|
|
6
6
|
<DetailGroup columns={2}>
|
|
@@ -27,7 +27,8 @@ validação, use `Field`.
|
|
|
27
27
|
|
|
28
28
|
`null`, `undefined`, string vazia ou só com espaços mostram um travessão; a leitura assistiva
|
|
29
29
|
recebe “Não informado”. Uma string em `empty` troca esse significado no domínio, enquanto um nó
|
|
30
|
-
React substitui a apresentação. `0` e `false` seguem como valores.
|
|
30
|
+
React substitui a apresentação. `0` e `false` seguem como valores. A regra é a mesma de
|
|
31
|
+
[`EmptyValue`](/ui/empty-value).
|
|
31
32
|
|
|
32
33
|
```tsx preview col
|
|
33
34
|
<DetailGroup columns={2}>
|
|
@@ -64,15 +65,21 @@ começam depois da mesma coluna de rótulo, com largura padrão de `7rem` em gru
|
|
|
64
65
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
65
66
|
|---|---|---|---|
|
|
66
67
|
| `variant` | `'plain' \| 'framed'` | `'plain'` | `framed` aplica a moldura canônica ao conjunto, com fundo transparente; na orientação horizontal, a coluna de rótulos recebe fundo sutil. |
|
|
67
|
-
| `dividers` | `boolean` | `false` |
|
|
68
|
+
| `dividers` | `boolean` | `false` | Linhas finas somente entre os campos, sem exigir moldura externa. |
|
|
68
69
|
| `columns` | `1 \| 2 \| 3 \| 4 \| 'auto'` | `1` | Número fixo de colunas ou distribuição automática por largura mínima. |
|
|
69
70
|
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Chave sobre o valor ou ao lado dele em cada campo. |
|
|
70
71
|
|
|
72
|
+
O grupo renderiza uma lista de definições (`<dl>`); os demais atributos, como `className`, vão para
|
|
73
|
+
ela.
|
|
74
|
+
|
|
71
75
|
## Propriedades de DetailField
|
|
72
76
|
|
|
73
77
|
| Propriedade | Tipo | Padrão | Descrição |
|
|
74
78
|
|---|---|---|---|
|
|
75
|
-
| `label` | `ReactNode` | | A chave do par. |
|
|
76
|
-
| `value` | `ReactNode` | | O valor; `null`, `undefined` e string vazia renderizam a ausência, `0` e `false` seguem como valores. |
|
|
79
|
+
| `label` | `ReactNode` | obrigatório | A chave do par. |
|
|
80
|
+
| `value` | `ReactNode` | obrigatório | O valor; `null`, `undefined` e string vazia renderizam a ausência, `0` e `false` seguem como valores. |
|
|
77
81
|
| `icon` | `ReactNode` | | Ícone decorativo antes do par chave/valor. |
|
|
78
82
|
| `empty` | `ReactNode` | `“Não informado”` | Uma string dá significado acessível ao travessão; um nó próprio substitui a apresentação. |
|
|
83
|
+
|
|
84
|
+
Os demais atributos, como `className`, vão para o contêiner do par. `DetailField` não aceita
|
|
85
|
+
`children`: o conteúdo entra por `value`.
|