@softize/opus 13.0.0 → 13.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.
Files changed (138) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/PROMOTED.md +46 -0
  3. package/README.md +28 -19
  4. package/bin/cli.mjs +87 -216
  5. package/bin/lib/cli-shared.mjs +131 -0
  6. package/bin/lib/db.mjs +16 -74
  7. package/bin/lib/gen-openapi.mjs +3 -3
  8. package/bin/lib/gen-runner.mjs +1 -1
  9. package/bin/lib/gen.mjs +14 -69
  10. package/bin/lib/mcp.mjs +3 -1
  11. package/bin/lib/seed.mjs +5 -62
  12. package/docs/ownership-vs-shadcn-lock.md +2 -3
  13. package/docs/protocol.md +7 -7
  14. package/docs/releasing.md +8 -2
  15. package/package.json +7 -3
  16. package/registry/templates/app/package.json +1 -1
  17. package/registry/templates/app/src/main.tsx +4 -4
  18. package/src/audit/drivers/console.ts +1 -0
  19. package/src/auth/drivers/better-auth.ts +1 -0
  20. package/src/auth/drivers/jwt.ts +1 -0
  21. package/src/cache/drivers/memory.ts +1 -0
  22. package/src/client/drivers/fetch.ts +2 -1
  23. package/src/core/actions.ts +6 -1
  24. package/src/core/audit.ts +9 -3
  25. package/src/core/contracts.ts +7 -0
  26. package/src/core/domain.ts +1 -1
  27. package/src/core/errors.ts +18 -15
  28. package/src/core/index.ts +4 -2
  29. package/src/core/package-version.ts +26 -0
  30. package/src/core/reactions.ts +1 -1
  31. package/src/core/runtime.ts +33 -23
  32. package/src/core/schedules.ts +1 -1
  33. package/src/core/types.ts +2 -2
  34. package/src/dsl/eval.ts +2 -2
  35. package/src/dsl/kysely.ts +2 -2
  36. package/src/dsl/loads.ts +1 -1
  37. package/src/dsl/parser.ts +5 -5
  38. package/src/events/drivers/mitt.ts +1 -0
  39. package/src/mcp/index.ts +2 -1
  40. package/src/observability/drivers/opentelemetry.ts +1 -0
  41. package/src/queue/drivers/bullmq.ts +3 -3
  42. package/src/scheduler/drivers/node-cron.ts +3 -2
  43. package/src/scheduler/every.ts +7 -7
  44. package/src/schema/openapi.ts +3 -3
  45. package/src/seed/index.ts +29 -0
  46. package/src/server/drivers/fastify.ts +5 -2
  47. package/src/server/drivers/node.ts +9 -6
  48. package/src/server/index.ts +3 -1
  49. package/src/storage/drivers/fs.ts +1 -0
  50. package/src/testing/index.ts +3 -3
  51. package/src/ui/components/patterns/confirm.tsx +2 -2
  52. package/src/ui/components/patterns/content-header.tsx +7 -1
  53. package/src/ui/components/patterns/data-state.tsx +1 -1
  54. package/src/ui/components/patterns/dock.tsx +20 -3
  55. package/src/ui/components/patterns/form.tsx +12 -8
  56. package/src/ui/components/patterns/list.tsx +4 -4
  57. package/src/ui/components/patterns/page.tsx +19 -1
  58. package/src/ui/components/patterns/shell-nav.tsx +10 -3
  59. package/src/ui/components/patterns/sidebar.tsx +17 -6
  60. package/src/ui/components/patterns/trigger.tsx +14 -16
  61. package/src/ui/components/patterns/view.tsx +26 -17
  62. package/src/ui/components/primitives/alert.tsx +11 -5
  63. package/src/ui/components/primitives/ask.tsx +3 -3
  64. package/src/ui/components/primitives/badge.tsx +11 -6
  65. package/src/ui/components/primitives/breadcrumb.tsx +2 -2
  66. package/src/ui/components/primitives/button.tsx +16 -3
  67. package/src/ui/components/primitives/calendar.tsx +28 -2
  68. package/src/ui/components/primitives/carousel.tsx +3 -3
  69. package/src/ui/components/primitives/chat.tsx +1 -1
  70. package/src/ui/components/primitives/checkbox.tsx +1 -1
  71. package/src/ui/components/primitives/command.tsx +2 -2
  72. package/src/ui/components/primitives/control.ts +12 -0
  73. package/src/ui/components/primitives/copyable.tsx +1 -1
  74. package/src/ui/components/primitives/dialog.tsx +12 -7
  75. package/src/ui/components/primitives/dot.tsx +5 -0
  76. package/src/ui/components/primitives/drawer.tsx +10 -3
  77. package/src/ui/components/primitives/field.tsx +3 -3
  78. package/src/ui/components/primitives/icon-picker.tsx +3 -1
  79. package/src/ui/components/primitives/input-group.tsx +1 -1
  80. package/src/ui/components/primitives/input-otp.tsx +1 -1
  81. package/src/ui/components/primitives/input.tsx +2 -2
  82. package/src/ui/components/primitives/progress.tsx +32 -3
  83. package/src/ui/components/primitives/radio-group.tsx +1 -1
  84. package/src/ui/components/primitives/resizable.tsx +3 -1
  85. package/src/ui/components/primitives/select.tsx +5 -5
  86. package/src/ui/components/primitives/slider.tsx +5 -1
  87. package/src/ui/components/primitives/sonner.tsx +3 -0
  88. package/src/ui/components/primitives/switch.tsx +1 -0
  89. package/src/ui/components/primitives/tabs.tsx +1 -0
  90. package/src/ui/components/primitives/textarea.tsx +1 -1
  91. package/src/ui/components/primitives/toggle.tsx +1 -1
  92. package/src/ui/components/primitives/tooltip.tsx +1 -0
  93. package/src/ui/docs/changelog.tsx +1 -1
  94. package/src/ui/docs/content/action-form.md +10 -3
  95. package/src/ui/docs/content/action-list.md +10 -1
  96. package/src/ui/docs/content/action-trigger.md +8 -1
  97. package/src/ui/docs/content/action-view.md +9 -2
  98. package/src/ui/docs/content/ask.md +11 -0
  99. package/src/ui/docs/content/calendar.md +13 -0
  100. package/src/ui/docs/content/card.md +26 -0
  101. package/src/ui/docs/content/chat.md +20 -0
  102. package/src/ui/docs/content/cli.md +71 -19
  103. package/src/ui/docs/content/composer.md +15 -0
  104. package/src/ui/docs/content/content.md +15 -0
  105. package/src/ui/docs/content/copyable.md +8 -0
  106. package/src/ui/docs/content/detail.md +19 -1
  107. package/src/ui/docs/content/dictionary-value.md +9 -2
  108. package/src/ui/docs/content/dock.md +8 -0
  109. package/src/ui/docs/content/dot.md +8 -0
  110. package/src/ui/docs/content/empty.md +2 -2
  111. package/src/ui/docs/content/getting-started.md +2 -2
  112. package/src/ui/docs/content/icon-picker.md +11 -0
  113. package/src/ui/docs/content/label.md +7 -0
  114. package/src/ui/docs/content/menu.md +6 -0
  115. package/src/ui/docs/content/metric-card.md +13 -0
  116. package/src/ui/docs/content/page.md +8 -0
  117. package/src/ui/docs/content/popover.md +6 -0
  118. package/src/ui/docs/content/progress.md +8 -11
  119. package/src/ui/docs/content/select.md +5 -5
  120. package/src/ui/docs/content/semantic-context.md +2 -2
  121. package/src/ui/docs/content/sidebar.md +14 -8
  122. package/src/ui/docs/content/skeleton.md +6 -0
  123. package/src/ui/docs/content/split.md +21 -0
  124. package/src/ui/docs/content/textarea.md +7 -0
  125. package/src/ui/docs/content/tokens.md +4 -4
  126. package/src/ui/docs/content/truncate.md +8 -0
  127. package/src/ui/docs/content/ui.md +14 -0
  128. package/src/ui/docs/doc-client.tsx +5 -5
  129. package/src/ui/docs/doc.tsx +26 -14
  130. package/src/ui/docs/registry.tsx +5 -5
  131. package/src/ui/docs/standalone.tsx +2 -2
  132. package/src/ui/drivers/react.tsx +17 -12
  133. package/src/ui/lib/action-errors.ts +45 -0
  134. package/src/ui/lib/zod-pt-br.ts +31 -4
  135. package/src/ui/meta.ts +3 -3
  136. package/src/ui/react.tsx +2 -0
  137. package/src/ui/theme.css +10 -8
  138. package/src/vite/design.ts +6 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softize/opus",
3
- "version": "13.0.0",
3
+ "version": "13.1.0",
4
4
  "description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -202,6 +202,7 @@
202
202
  "docs",
203
203
  "README.md",
204
204
  "CHANGELOG.md",
205
+ "PROMOTED.md",
205
206
  "LICENSE"
206
207
  ],
207
208
  "publishConfig": {
@@ -259,7 +260,8 @@
259
260
  "node-cron": "^3.0.0",
260
261
  "pg": "^8.0.0",
261
262
  "pino": "^9.0.0",
262
- "react": "^18.0.0 || ^19.0.0",
263
+ "react": "^19.0.0",
264
+ "react-dom": "^19.0.0",
263
265
  "react-hook-form": "^7.0.0",
264
266
  "vite": "^5.0.0 || ^6.0.0",
265
267
  "zod": "^3.24.0"
@@ -301,6 +303,9 @@
301
303
  "react": {
302
304
  "optional": true
303
305
  },
306
+ "react-dom": {
307
+ "optional": true
308
+ },
304
309
  "vite": {
305
310
  "optional": true
306
311
  },
@@ -328,7 +333,6 @@
328
333
  "@tanstack/react-query": "^5.62.0",
329
334
  "@testing-library/dom": "^10.4.0",
330
335
  "@testing-library/react": "^16.1.0",
331
- "@types/better-sqlite3": "^7.6.0",
332
336
  "@types/jsonwebtoken": "^9.0.0",
333
337
  "@types/node": "^22.0.0",
334
338
  "@types/node-cron": "^3.0.0",
@@ -30,7 +30,7 @@
30
30
  "zod": "^3.24.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@softize/base": "^2.1.0",
33
+ "@softize/base": "^2.2.1",
34
34
  "@tailwindcss/vite": "^4.1.0",
35
35
  "@types/node": "^22.0.0",
36
36
  "@types/react": "^19.0.0",
@@ -1,13 +1,13 @@
1
1
  import { StrictMode } from 'react'
2
2
  import { createRoot } from 'react-dom/client'
3
3
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
4
- import { TbdlibProvider, TooltipProvider } from '@softize/opus/ui/react'
4
+ import { OpusProvider, TooltipProvider } from '@softize/opus/ui/react'
5
5
  import { fetchClient } from '@softize/opus/client/fetch'
6
6
  import { App } from './App.tsx'
7
7
  import './index.css'
8
8
 
9
9
  // O trio de providers do padrão da casa — os hooks (useTriggerAction/useFormAction…)
10
- // exigem QueryClient + Tbdlib, e qualquer Tooltip da base exige o TooltipProvider.
10
+ // exigem QueryClient + OpusProvider, e qualquer Tooltip da base exige o TooltipProvider.
11
11
  const queryClient = new QueryClient()
12
12
  // Same-origin (/api). Quando o app ganhar server próprio, aponte o baseUrl pra ele.
13
13
  const client = fetchClient({ baseUrl: '' })
@@ -15,11 +15,11 @@ const client = fetchClient({ baseUrl: '' })
15
15
  createRoot(document.getElementById('root')!).render(
16
16
  <StrictMode>
17
17
  <QueryClientProvider client={queryClient}>
18
- <TbdlibProvider client={client}>
18
+ <OpusProvider client={client}>
19
19
  <TooltipProvider>
20
20
  <App />
21
21
  </TooltipProvider>
22
- </TbdlibProvider>
22
+ </OpusProvider>
23
23
  </QueryClientProvider>
24
24
  </StrictMode>,
25
25
  )
@@ -40,6 +40,7 @@ export interface ConsoleAuditOptions {
40
40
  redact?: (value: unknown) => unknown
41
41
  }
42
42
 
43
+ /** Sink de auditoria que imprime cada registro no console, com redação opcional de campos sensíveis. */
43
44
  export function consoleAudit(options: ConsoleAuditOptions = {}): AuditSink {
44
45
  const {
45
46
  format = defaultFormat(),
@@ -40,6 +40,7 @@ export interface BetterAuthDriverOptions {
40
40
 
41
41
  const denyAll: CanFn = () => false
42
42
 
43
+ /** AuthAdapter que valida a sessão no IdP better-auth (`/api/auth/get-session`) com o cookie da request. */
43
44
  export function betterAuthSession(options: BetterAuthDriverOptions): AuthAdapter {
44
45
  const { baseURL, mapUser, mapTenant, can, timeoutMs = 5000, name = 'better-auth' } = options
45
46
  const base = baseURL.replace(/\/+$/, '')
@@ -79,6 +79,7 @@ export interface JwtAuthOptions<P extends JwtPayload = JwtPayload> {
79
79
  // Adapter factory
80
80
  // =============================================================================
81
81
 
82
+ /** AuthAdapter que verifica um JWT (Bearer) e projeta o payload em `User` + `can`. */
82
83
  export function jwtAuth<P extends JwtPayload = JwtPayload>(
83
84
  options: JwtAuthOptions<P>,
84
85
  ): AuthAdapter {
@@ -31,6 +31,7 @@ export interface MemoryCache extends CacheAdapter {
31
31
  size(): number
32
32
  }
33
33
 
34
+ /** CacheAdapter em memória do processo, com TTL por entrada e limite opcional de itens; adequado a dev e testes. */
34
35
  export function memoryCache(options: MemoryCacheOptions = {}): MemoryCache {
35
36
  const entries = new Map<string, Entry>()
36
37
  const maxEntries = options.maxEntries ?? 10_000
@@ -50,6 +50,7 @@ export interface FetchClientOptions {
50
50
  // Adapter factory
51
51
  // =============================================================================
52
52
 
53
+ /** ClientAdapter que chama actions via HTTP `fetch`, classificando falhas de transporte como `dependency`. */
53
54
  export function fetchClient(options: FetchClientOptions): ClientAdapter {
54
55
  const {
55
56
  baseUrl,
@@ -92,7 +93,7 @@ export function fetchClient(options: FetchClientOptions): ClientAdapter {
92
93
  error({
93
94
  code: 'client.network',
94
95
  category: 'dependency',
95
- message: 'Network request failed',
96
+ message: `Falha de rede ao chamar ${action.name}`,
96
97
  cause: String(cause),
97
98
  }),
98
99
  )
@@ -1,9 +1,14 @@
1
1
  /**
2
- * tbdlib — `defineAction` factory + type guards
2
+ * Opus — `defineAction` factory + type guards
3
3
  *
4
4
  * `defineAction` é identity function tipada que aceita qualquer variante
5
5
  * de `ActionDef` e preserva o subtipo exato pra type narrowing posterior.
6
6
  *
7
+ * Quando cliente e servidor consomem a mesma action, o caminho canônico é
8
+ * `defineContract` (parte compartilhável) + `bindAction` (handler, loads e o
9
+ * resto server-only) — ver `contracts.ts`. `defineAction` continua válido e
10
+ * suportado para action server-only, em que não há contrato a compartilhar.
11
+ *
7
12
  * Validação estrutural acontece no `Runtime.register()`, não aqui. Esta função
8
13
  * é puramente sobre **declaração**.
9
14
  *
package/src/core/audit.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — AuditEmitter
2
+ * Opus — AuditEmitter
3
3
  *
4
4
  * Classe que coordena a emissão de `AuditRecord` pros `AuditSink` registrados.
5
5
  * Aplica config da action (redact, fields, severity, sink filter) antes de
@@ -71,11 +71,17 @@ export class AuditEmitter {
71
71
  if (failures.length === 0) return
72
72
 
73
73
  if (this.mode === 'strict') {
74
+ this.log.error('audit sinks failed (strict mode)', {
75
+ failures: failures.length,
76
+ total: targetSinks.length,
77
+ reasons: failures.map((f) => String(f.reason)),
78
+ })
74
79
  throw error({
75
80
  code: 'audit.sink.failed',
76
81
  category: 'internal',
77
- message: `${failures.length}/${targetSinks.length} audit sinks failed`,
78
- meta: { failures: failures.map((f) => String(f.reason)) },
82
+ message: `${failures.length} de ${targetSinks.length} sinks de auditoria falharam`,
83
+ // o número vai à wire; o motivo cru de cada sink fica no log do servidor.
84
+ meta: { failures: failures.length },
79
85
  })
80
86
  }
81
87
 
@@ -64,6 +64,7 @@ export type ViewContract<In = unknown, Out = unknown, ParsedIn = any> = Omit<
64
64
  BindingKeys
65
65
  >
66
66
 
67
+ /** União dos contratos por kind — o que `bindAction` aceita e a UI tipada consome. */
67
68
  export type ActionContract<In = any, Out = any, ParsedIn = any> =
68
69
  | SimpleContract<In, Out, ParsedIn>
69
70
  | FormContract<In & Record<string, unknown>, Out, ParsedIn>
@@ -97,6 +98,12 @@ export interface ActionBinding<ParsedIn, Out> {
97
98
  // defineContract (overloads por kind, igual defineAction)
98
99
  // =============================================================================
99
100
 
101
+ /**
102
+ * Declara a parte compartilhável de uma action (identidade, schemas, docs e
103
+ * `authorize` action-level) sem handler nem dependência server-only — pode
104
+ * ser importada pelo cliente. Identity function tipada; o kind é inferido
105
+ * pelo overload. Complete com `bindAction` no servidor.
106
+ */
100
107
  export function defineContract<In, Out, ParsedIn = In>(
101
108
  contract: SimpleContract<In, Out, ParsedIn>,
102
109
  ): SimpleContract<In, Out, ParsedIn>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — `defineDomain` factory + flatten helper.
2
+ * Opus — `defineDomain` factory + flatten helper.
3
3
  *
4
4
  * Domain agrupa as peças que pertencem a um mesmo recorte funcional
5
5
  * (dicts, entities, repository, service, actions, reactions, schedules,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — Error factory
2
+ * Opus — Error factory
3
3
  *
4
4
  * `error()` é a maneira canônica de emitir `ActionError` de dentro de um handler.
5
5
  * Aplica defaults sensatos baseados na categoria (severity, retriable) e
@@ -9,11 +9,12 @@
9
9
  * throw error({
10
10
  * code: 'deal.archive.notFound',
11
11
  * category: 'not_found',
12
- * message: `Deal ${id} not found`,
12
+ * message: `Negócio ${id} não encontrado`,
13
13
  * })
14
14
  *
15
15
  * Erros não criados via `error()` (ex: `throw new Error(...)`, exceções de driver)
16
- * são normalizados pelo runtime em `{ category: 'internal', code: 'internal.unhandled' }`.
16
+ * são normalizados pelo runtime em `{ category: 'internal', code: 'internal.unhandled' }`
17
+ * com a microcopy fixa `UNHANDLED_ERROR_MESSAGE`; o texto original vai só para `cause`.
17
18
  */
18
19
 
19
20
  import type {
@@ -24,7 +25,7 @@ import type {
24
25
  } from './types.ts'
25
26
 
26
27
  /** Brand interno pra distinguir ActionError construído via `error()` de Error genérico. */
27
- const ACTION_ERROR_BRAND = Symbol('tbdlib.ActionError')
28
+ const ACTION_ERROR_BRAND = Symbol('opus.ActionError')
28
29
 
29
30
  /**
30
31
  * Defaults de severity por categoria.
@@ -148,27 +149,29 @@ export function isActionError(value: unknown): value is ActionError {
148
149
  )
149
150
  }
150
151
 
152
+ /**
153
+ * Microcopy que a pessoa recebe quando um handler falha com exceção não tratada.
154
+ * Fixa de propósito: a mensagem crua (driver, SQL, stack) fica no logger do
155
+ * runtime e em `cause`, nunca vira o texto exibido.
156
+ */
157
+ export const UNHANDLED_ERROR_MESSAGE =
158
+ 'Não foi possível concluir a operação. Tente novamente.'
159
+
151
160
  /**
152
161
  * Normaliza uma exceção qualquer em ActionError. Usado pelo runtime quando
153
162
  * handler lança algo que não foi criado via `error()`.
163
+ *
164
+ * A `message` é sempre `UNHANDLED_ERROR_MESSAGE`; o valor lançado é preservado
165
+ * em `cause` (só a mensagem quando for `Error` — stack não vaza pra wire).
154
166
  */
155
167
  export function normalizeError(thrown: unknown): ActionError {
156
168
  if (isActionError(thrown)) return thrown
157
169
 
158
- if (thrown instanceof Error) {
159
- return error({
160
- code: 'internal.unhandled',
161
- category: 'internal',
162
- message: thrown.message,
163
- cause: thrown.message, // só mensagem; stack não vaza pra wire
164
- })
165
- }
166
-
167
170
  return error({
168
171
  code: 'internal.unhandled',
169
172
  category: 'internal',
170
- message: typeof thrown === 'string' ? thrown : 'Unhandled error',
171
- cause: thrown,
173
+ message: UNHANDLED_ERROR_MESSAGE,
174
+ cause: thrown instanceof Error ? thrown.message : thrown,
172
175
  })
173
176
  }
174
177
 
package/src/core/index.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * @softize/opus/core — public entry
3
3
  *
4
- * Re-exports da superfície pública. Source of truth: `/docs/protocol.md`.
4
+ * Re-exports da superfície pública. A fonte de verdade são as declarações
5
+ * (`defineContract`, `bindAction`, `defineDomain`…); `/docs/protocol.md`, o
6
+ * manifest e o OpenAPI são projeções delas.
5
7
  */
6
8
 
7
9
  // — Tipos —————————————————————————————————————————————————————————————————————
@@ -128,7 +130,7 @@ export type {
128
130
  } from './types.ts'
129
131
 
130
132
  // — Erro ——————————————————————————————————————————————————————————————————————
131
- export { error, isActionError, normalizeError } from './errors.ts'
133
+ export { error, isActionError, normalizeError, UNHANDLED_ERROR_MESSAGE } from './errors.ts'
132
134
  export type { ErrorInput } from './errors.ts'
133
135
  export { normalizeTraceContext } from './trace.ts'
134
136
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Opus — versão do pacote lida do `package.json` que viaja junto do source.
3
+ *
4
+ * Server-only (usa `node:fs`); não é re-exportado por `core/index.ts`. Usado
5
+ * pelos drivers de servidor (info do OpenAPI), pelo servidor MCP e pelo plugin
6
+ * de design pra identificar a versão do SDK sem duplicar leitura de arquivo.
7
+ */
8
+
9
+ import { readFileSync } from 'node:fs'
10
+
11
+ const FALLBACK_VERSION = '0.0.0'
12
+
13
+ /** Versão de `@softize/opus` resolvida do `package.json` real; `0.0.0` se a leitura falhar. */
14
+ export function readPackageVersion(): string {
15
+ // `import.meta.url` sobrevive ao bundling do vite.config (o vite injeta a
16
+ // URL original do arquivo), então o package.json resolve do source real.
17
+ try {
18
+ const pkg = JSON.parse(
19
+ readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
20
+ ) as { version?: string }
21
+ return pkg.version ?? FALLBACK_VERSION
22
+ /* v8 ignore next 3 — defensivo; o package.json viaja junto do source */
23
+ } catch {
24
+ return FALLBACK_VERSION
25
+ }
26
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — `defineReaction` factory + type guards
2
+ * Opus — `defineReaction` factory + type guards
3
3
  *
4
4
  * `defineReaction` é identity function tipada que aceita uma `ReactionDef`
5
5
  * e preserva o tipo do evento pra inferência. Runtime registra no
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — Runtime
2
+ * Opus — Runtime
3
3
  *
4
4
  * Orquestrador central. Recebe actions + reactions + adapters + config no setup,
5
5
  * registra cada peça, e executa o pipeline padrão (validate → load → auth →
@@ -281,7 +281,7 @@ export class Runtime {
281
281
  this.config = applyConfigDefaults(setup.config)
282
282
  this.logger = setup.logger
283
283
  this.observability = setup.observability
284
- this.log = setup.logger ?? new ConsoleLogger({ runtime: 'tbdlib' })
284
+ this.log = setup.logger ?? new ConsoleLogger({ runtime: 'opus' })
285
285
 
286
286
  this.audit = new AuditEmitter({ mode: this.config.auditMode, log: this.log })
287
287
  if (setup.audit !== undefined) {
@@ -345,7 +345,7 @@ export class Runtime {
345
345
  throw error({
346
346
  code: 'runtime.duplicate_loader',
347
347
  category: 'internal',
348
- message: `Loader resolver "${entity}" already registered`,
348
+ message: ` existe um resolver de loader registrado para "${entity}"`,
349
349
  })
350
350
  }
351
351
  this.loaderResolvers.set(entity, resolver)
@@ -368,7 +368,7 @@ export class Runtime {
368
368
  throw error({
369
369
  code: 'runtime.duplicate_action',
370
370
  category: 'internal',
371
- message: `Action "${action.name}" registered twice`,
371
+ message: `Action "${action.name}" registrada duas vezes`,
372
372
  })
373
373
  }
374
374
  this.actions.set(action.name, action)
@@ -381,7 +381,7 @@ export class Runtime {
381
381
  throw error({
382
382
  code: 'runtime.duplicate_reaction',
383
383
  category: 'internal',
384
- message: `Reaction "${reaction.name}" registered twice`,
384
+ message: `Reaction "${reaction.name}" registrada duas vezes`,
385
385
  })
386
386
  }
387
387
  this.reactions.set(reaction.name, reaction)
@@ -394,7 +394,7 @@ export class Runtime {
394
394
  throw error({
395
395
  code: 'runtime.duplicate_schedule',
396
396
  category: 'internal',
397
- message: `Schedule "${schedule.name}" registered twice`,
397
+ message: `Schedule "${schedule.name}" registrado duas vezes`,
398
398
  })
399
399
  }
400
400
  this.schedules.set(schedule.name, schedule)
@@ -410,7 +410,7 @@ export class Runtime {
410
410
  throw error({
411
411
  code: 'runtime.already_started',
412
412
  category: 'internal',
413
- message: 'Runtime.start() called twice',
413
+ message: 'Runtime.start() foi chamado mais de uma vez',
414
414
  })
415
415
  }
416
416
 
@@ -431,7 +431,7 @@ export class Runtime {
431
431
  throw error({
432
432
  code: 'runtime.eventbus_required',
433
433
  category: 'internal',
434
- message: `${this.reactions.size} reaction(s) registered without an EventBusAdapter`,
434
+ message: `${this.reactions.size} reaction(s) registrada(s) sem um EventBusAdapter configurado`,
435
435
  })
436
436
  }
437
437
 
@@ -441,7 +441,7 @@ export class Runtime {
441
441
  throw error({
442
442
  code: 'runtime.scheduler_required',
443
443
  category: 'internal',
444
- message: `${this.schedules.size} schedule(s) registered without a SchedulerAdapter`,
444
+ message: `${this.schedules.size} schedule(s) registrado(s) sem um SchedulerAdapter configurado`,
445
445
  })
446
446
  }
447
447
 
@@ -514,7 +514,7 @@ export class Runtime {
514
514
  return this.errorResult(actionName, error({
515
515
  code: 'runtime.action_not_found',
516
516
  category: 'not_found',
517
- message: `Action "${actionName}" is not registered`,
517
+ message: `Action "${actionName}" não está registrada`,
518
518
  }), 0, ctxBase.requestId)
519
519
  }
520
520
 
@@ -535,21 +535,21 @@ export class Runtime {
535
535
  return this.errorResult(spec.action, error({
536
536
  code: 'runtime.action_not_found',
537
537
  category: 'not_found',
538
- message: `Action "${spec.action}" is not registered`,
538
+ message: `Action "${spec.action}" não está registrada`,
539
539
  }), 0, spec.ctx.requestId)
540
540
  }
541
541
  if (!isBackgroundAction(action)) {
542
542
  return this.errorResult(spec.action, error({
543
543
  code: 'runtime.action_not_background',
544
544
  category: 'validation',
545
- message: `Action "${spec.action}" is not declared as background`,
545
+ message: `Action "${spec.action}" não está declarada como background`,
546
546
  }), 0, spec.ctx.requestId)
547
547
  }
548
548
  if ((ctxBase.user?.id ?? null) !== spec.ctx.userId) {
549
549
  return this.errorResult(spec.action, error({
550
550
  code: 'runtime.job_actor_mismatch',
551
551
  category: 'authentication',
552
- message: 'Worker context actor does not match the job envelope',
552
+ message: 'O ator do contexto do worker não corresponde ao envelope do job',
553
553
  }), 0, spec.ctx.requestId)
554
554
  }
555
555
 
@@ -604,7 +604,7 @@ export class Runtime {
604
604
  throw error({
605
605
  code: 'auth.unauthenticated',
606
606
  category: 'authentication',
607
- message: 'Authentication required',
607
+ message: 'Autenticação necessária',
608
608
  })
609
609
  }
610
610
 
@@ -616,7 +616,7 @@ export class Runtime {
616
616
  throw error({
617
617
  code: 'auth.forbidden',
618
618
  category: 'authorization',
619
- message: 'Forbidden',
619
+ message: 'Acesso negado',
620
620
  })
621
621
  }
622
622
  if (decision !== true) throw decision
@@ -627,7 +627,7 @@ export class Runtime {
627
627
  throw error({
628
628
  code: 'runtime.queue_required',
629
629
  category: 'internal',
630
- message: `Background action "${action.name}" requires a QueueAdapter`,
630
+ message: `A action background "${action.name}" exige um QueueAdapter`,
631
631
  })
632
632
  }
633
633
  const background = action.background
@@ -635,7 +635,7 @@ export class Runtime {
635
635
  throw error({
636
636
  code: 'runtime.invalid_background_config',
637
637
  category: 'internal',
638
- message: `Background action "${action.name}" has no background config`,
638
+ message: `A action background "${action.name}" não tem configuração de background`,
639
639
  })
640
640
  }
641
641
  const spec: JobSpec = {
@@ -729,6 +729,14 @@ export class Runtime {
729
729
  meta: this.buildMeta(actionId, action.name, durationMs, ctxBase.requestId, trace),
730
730
  }
731
731
  } catch (thrown) {
732
+ // Exceção fora de `error()` vira microcopy fixa no envelope; o texto
733
+ // original só sobrevive aqui, no logger da action, e em `cause`.
734
+ if (!isActionError(thrown)) {
735
+ ctx.log.error('action failed with unhandled error', {
736
+ error: thrown instanceof Error ? thrown.message : String(thrown),
737
+ ...(thrown instanceof Error && thrown.stack !== undefined ? { stack: thrown.stack } : {}),
738
+ })
739
+ }
732
740
  const actionError = normalizeError(thrown)
733
741
  const durationMs = performance.now() - startedAt
734
742
  await this.audit.emit(
@@ -898,7 +906,7 @@ export class Runtime {
898
906
  throw error({
899
907
  code: 'emit.no_bus',
900
908
  category: 'internal',
901
- message: `ctx.emit('${event}') called without EventBusAdapter`,
909
+ message: `ctx.emit('${event}') chamado sem EventBusAdapter configurado`,
902
910
  })
903
911
  }
904
912
  this.log.warn('emit dropped — no EventBusAdapter', { event })
@@ -931,7 +939,7 @@ export class Runtime {
931
939
  throw error({
932
940
  code: 'emit.failed',
933
941
  category: 'internal',
934
- message: `failed to publish event "${event}"`,
942
+ message: `Falha ao publicar o evento "${event}"`,
935
943
  cause: String(err),
936
944
  })
937
945
  }
@@ -953,7 +961,7 @@ export class Runtime {
953
961
  throw error({
954
962
  code: kind === 'input' ? 'validation.invalid_input' : 'validation.invalid_output',
955
963
  category: kind === 'input' ? 'validation' : 'internal',
956
- message: kind === 'input' ? 'Input validation failed' : 'Output validation failed (dev)',
964
+ message: kind === 'input' ? 'Dados de entrada inválidos' : 'Dados de saída inválidos (validação em dev)',
957
965
  issues: result.issues.map((iss) => ({
958
966
  path: (iss.path ?? []).join('.'),
959
967
  code: 'invalid',
@@ -978,7 +986,7 @@ export class Runtime {
978
986
  throw error({
979
987
  code: `${action.name}.${key}.not_found`,
980
988
  category: 'not_found',
981
- message: `Resource "${key}" not found for action "${action.name}"`,
989
+ message: `Recurso "${key}" não encontrado para a action "${action.name}"`,
982
990
  })
983
991
  }
984
992
  loaded[key] = value
@@ -1128,7 +1136,7 @@ export class Runtime {
1128
1136
  throw error({
1129
1137
  code: 'runtime.subscribe_unsupported',
1130
1138
  category: 'internal',
1131
- message: 'EventBusAdapter does not support subscribe()',
1139
+ message: 'O EventBusAdapter não suporta subscribe()',
1132
1140
  })
1133
1141
  }
1134
1142
  const patterns = Array.isArray(reaction.on) ? reaction.on : [reaction.on]
@@ -1222,10 +1230,12 @@ export class Runtime {
1222
1230
  },
1223
1231
  )
1224
1232
  } catch (err) {
1225
- const actionError = isActionError(err) ? err : normalizeError(err)
1233
+ const actionError = normalizeError(err)
1226
1234
  reactionLog.error('reaction failed', {
1227
1235
  code: actionError.code,
1228
1236
  message: actionError.message,
1237
+ // Erro não tratado tem microcopy fixa; o texto original está em `cause`.
1238
+ ...(actionError.cause !== undefined ? { cause: actionError.cause } : {}),
1229
1239
  })
1230
1240
  }
1231
1241
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — `defineSchedule` factory + type guards.
2
+ * Opus — `defineSchedule` factory + type guards.
3
3
  *
4
4
  * Schedule é a primitiva pra **ação iniciada pelo próprio sistema** — sem
5
5
  * trigger externo (HTTP, evento, AI). Usado pra monitoramento, relatórios,
package/src/core/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tbdlib — Core types
2
+ * Opus — Core types
3
3
  *
4
4
  * Tipos centrais do protocolo. Source of truth: `/docs/protocol.md`.
5
5
  * Qualquer divergência entre este arquivo e a doc é bug; alinhar a doc primeiro,
@@ -250,7 +250,7 @@ export interface User {
250
250
 
251
251
  /**
252
252
  * Função que checa permissão. Plugada pelo `AuthAdapter`.
253
- * tbdlib não implementa RBAC/ABAC — apenas delega.
253
+ * O Opus não implementa RBAC/ABAC — apenas delega.
254
254
  */
255
255
  export type CanFn = (permission: string, resource?: unknown) => boolean | Promise<boolean>
256
256
 
package/src/dsl/eval.ts CHANGED
@@ -64,7 +64,7 @@ export function evalExpression(node: AstNode, ctx: EvalContext): unknown {
64
64
  return (left as number) / (right as number)
65
65
  /* v8 ignore next 2 */
66
66
  default:
67
- throw new Error(`DSL eval: unknown binary op ${node.op as string}`)
67
+ throw new Error(`DSL eval: operador binário desconhecido ${node.op as string}`)
68
68
  }
69
69
  }
70
70
  case 'in': {
@@ -132,5 +132,5 @@ function callFunction(name: string, args: unknown[]): unknown {
132
132
  }
133
133
  return null
134
134
  }
135
- throw new Error(`DSL eval: unknown function '${name}'`)
135
+ throw new Error(`DSL eval: função desconhecida '${name}'`)
136
136
  }
package/src/dsl/kysely.ts CHANGED
@@ -136,7 +136,7 @@ function compile(node: AstNode, eb: KyselyEb, bindings: KyselyBindings): any {
136
136
  return (left as number) / (right as number)
137
137
  /* v8 ignore next 2 */
138
138
  default:
139
- throw new Error(`DSL kysely: unhandled binary ${node.op as string}`)
139
+ throw new Error(`DSL kysely: operador binário não tratado ${node.op as string}`)
140
140
  }
141
141
  }
142
142
  case 'in': {
@@ -195,7 +195,7 @@ function compile(node: AstNode, eb: KyselyEb, bindings: KyselyBindings): any {
195
195
  `DSL kysely: function '${node.name}' precisa de mapping de coluna explícito (use eb.fn)`,
196
196
  )
197
197
  }
198
- throw new Error(`DSL kysely: unknown function '${node.name}'`)
198
+ throw new Error(`DSL kysely: função desconhecida '${node.name}'`)
199
199
  }
200
200
  }
201
201
  }
package/src/dsl/loads.ts CHANGED
@@ -31,7 +31,7 @@ export function parseLoad(src: string): LoadSpec {
31
31
  const trimmed = src.trim()
32
32
  const m = LOAD_RE.exec(trimmed)
33
33
  if (m === null) {
34
- throw new Error(`DSL loads: invalid syntax '${src}'`)
34
+ throw new Error(`DSL loads: sintaxe inválida '${src}'`)
35
35
  }
36
36
  const entity = m[1]!
37
37
  const argsRaw = m[2]!.trim()