@voltro/cli 0.31.0 → 0.32.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 (74) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/dist/{apiBuild-CLPt-pX6.js → apiBuild-C4uEbs3s.js} +2 -2
  3. package/dist/apiBuild-OJEjtwcn.js +2 -0
  4. package/dist/bin.js +3 -3
  5. package/dist/checkCommand-CwMrzAgV.js +2 -0
  6. package/dist/{checkCommand-D7r6_pk7.js → checkCommand-dm7OHtPt.js} +328 -288
  7. package/dist/{commands-CNVVv03h.js → commands-C0nEePif.js} +53 -11
  8. package/dist/dbCommand-By__Ev0R.js +2 -0
  9. package/dist/{dbCommand-BH8GAYuf.js → dbCommand-ifOMafuG.js} +90 -82
  10. package/dist/{dev-DdQEn2Ia.js → dev-5ficNnvF.js} +1232 -1224
  11. package/dist/{dev-DOZVMVfi.js → dev-rc3fwPSZ.js} +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/{inspectMetrics-BeEAnjqw.js → inspectMetrics-BqO4E9G0.js} +757 -715
  14. package/dist/{sdkgen-v9WV8zRi.js → sdkgen-B_5mHQS2.js} +1 -1
  15. package/dist/{serveCommand-KsKXXWn4.js → serveCommand-PxMmn96o.js} +366 -366
  16. package/dist/serveEntry.js +2 -2
  17. package/dist/{start-CI2QdKy9.js → start-D1-8eKrO.js} +1 -1
  18. package/dist/startEntry.js +2 -2
  19. package/package.json +417 -17
  20. package/templates/AGENTS.md +1 -1
  21. package/templates/agent-docs/_index.md +1 -1
  22. package/templates/agent-docs/plugins/audit.md +24 -0
  23. package/templates/agent-docs/security.md +9 -0
  24. package/templates/agent-docs/whats-new.md +109 -61
  25. package/templates/apps/api-ai/package.json +7 -7
  26. package/templates/apps/api-auth/package.json +8 -8
  27. package/templates/apps/api-backend/package.json +7 -7
  28. package/templates/apps/api-backend-deactivation/package.json +7 -7
  29. package/templates/apps/api-backend-mail/package.json +8 -8
  30. package/templates/apps/api-backend-mariadb/package.json +9 -9
  31. package/templates/apps/api-backend-sqlite/package.json +8 -8
  32. package/templates/apps/api-backend-storage/package.json +8 -8
  33. package/templates/apps/api-cms/package.json +10 -10
  34. package/templates/apps/api-collab/package.json +8 -8
  35. package/templates/apps/api-data-advanced/package.json +8 -8
  36. package/templates/apps/api-durable/package.json +8 -8
  37. package/templates/apps/api-feature-flags/package.json +9 -9
  38. package/templates/apps/api-governance/package.json +8 -8
  39. package/templates/apps/api-kv/package.json +8 -8
  40. package/templates/apps/api-moderation/package.json +8 -8
  41. package/templates/apps/api-observability/package.json +8 -8
  42. package/templates/apps/api-ratelimit/package.json +8 -8
  43. package/templates/apps/api-rbac/package.json +8 -8
  44. package/templates/apps/api-rest/package.json +7 -7
  45. package/templates/apps/api-saas/package.json +11 -11
  46. package/templates/apps/api-saas-starter/package.json +10 -10
  47. package/templates/apps/api-search/package.json +8 -8
  48. package/templates/apps/api-status/package.json +8 -8
  49. package/templates/apps/api-versioning/package.json +8 -8
  50. package/templates/apps/api-webhooks/package.json +9 -9
  51. package/templates/apps/changelog/package.json +6 -6
  52. package/templates/apps/edge-functions/package.json +2 -2
  53. package/templates/apps/frontend-admin/package.json +8 -8
  54. package/templates/apps/frontend-app/package.json +8 -8
  55. package/templates/apps/frontend-auth/package.json +8 -8
  56. package/templates/apps/frontend-blank/package.json +7 -7
  57. package/templates/apps/frontend-cms/package.json +9 -9
  58. package/templates/apps/frontend-collab/package.json +9 -9
  59. package/templates/apps/frontend-contact/package.json +7 -7
  60. package/templates/apps/frontend-dashboard/package.json +7 -7
  61. package/templates/apps/frontend-docs/package.json +7 -7
  62. package/templates/apps/frontend-i18n/package.json +6 -6
  63. package/templates/apps/frontend-landing/package.json +7 -7
  64. package/templates/apps/frontend-portal/package.json +8 -8
  65. package/templates/apps/frontend-saas/package.json +8 -8
  66. package/templates/apps/frontend-spa/package.json +7 -7
  67. package/templates/apps/frontend-ssr/package.json +7 -7
  68. package/templates/apps/frontend-ssr-api/package.json +8 -8
  69. package/templates/apps/frontend-static-blog/package.json +6 -6
  70. package/templates/apps/frontend-status/package.json +8 -8
  71. package/templates/apps/mobile-app/package.json +2 -2
  72. package/dist/apiBuild-BOqL1dlg.js +0 -2
  73. package/dist/checkCommand-ClRvH_SN.js +0 -2
  74. package/dist/dbCommand-BwQ_Jl98.js +0 -2
@@ -1,5 +1,5 @@
1
- import { ct as e } from "./inspectMetrics-BeEAnjqw.js";
1
+ import { ct as e } from "./inspectMetrics-BqO4E9G0.js";
2
2
  import { c as t } from "./seedRunner-Bqxgp7HZ.js";
3
3
  import { r as n } from "./appModuleLoader-C9r9mxZt.js";
4
- import { t as r } from "./serveCommand-KsKXXWn4.js";
4
+ import { t as r } from "./serveCommand-PxMmn96o.js";
5
5
  export { e as loadDotEnv, n as registerAppModules, t as registerDriver, r as runServe };
@@ -1,4 +1,4 @@
1
- import { $ as e, C as t, Ct as n, D as r, E as i, G as a, J as o, K as s, L as c, P as l, Q as u, S as d, St as ee, Tt as f, V as p, W as m, _ as h, _t as g, a as _, b as v, bt as y, c as b, dt as te, f as x, ft as S, g as C, h as w, i as T, k as E, m as D, mt as O, o as ne, p as re, pt as k, q as ie, r as A, s as j, t as M, ut as N, v as ae, wt as oe, x as P, y as F } from "./inspectMetrics-BeEAnjqw.js";
1
+ import { $ as e, C as t, Ct as n, D as r, E as i, G as a, J as o, K as s, L as c, P as l, Q as u, S as d, St as ee, Tt as f, V as p, W as m, _ as h, _t as g, a as _, b as v, bt as y, c as b, dt as te, f as x, ft as S, g as C, h as w, i as T, k as E, m as D, mt as O, o as ne, p as re, pt as k, q as ie, r as A, s as j, t as M, ut as N, v as ae, wt as oe, x as P, y as F } from "./inspectMetrics-BqO4E9G0.js";
2
2
  import { A as se, E as I, O as ce, a as le, k as L, m as ue } from "./inspect-BUUjt773.js";
3
3
  import { t as de } from "./bootTiming-BdyP9nYw.js";
4
4
  import { dirname as fe, extname as pe, join as R, resolve as z } from "node:path";
@@ -1,3 +1,3 @@
1
- import { ct as e } from "./inspectMetrics-BeEAnjqw.js";
2
- import { t } from "./start-CI2QdKy9.js";
1
+ import { ct as e } from "./inspectMetrics-BqO4E9G0.js";
2
+ import { t } from "./start-D1-8eKrO.js";
3
3
  export { e as loadDotEnv, t as runStartCommand };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/cli",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -55,6 +55,406 @@
55
55
  "main": "./dist/index.js",
56
56
  "module": "./dist/index.js",
57
57
  "types": "./dist/index.d.ts",
58
+ "voltro": {
59
+ "codemods": [
60
+ {
61
+ "version": "0.10.0",
62
+ "id": "0.10.0/01_render-mode-client-to-spa",
63
+ "title": "renderMode is a closed set — `'client'` becomes `'spa'`",
64
+ "kind": "transform"
65
+ },
66
+ {
67
+ "version": "0.10.0",
68
+ "id": "0.10.0/02_router-state-carries-its-path",
69
+ "title": "`renderRouterStateScript` now requires the `path` it rendered",
70
+ "kind": "manual"
71
+ },
72
+ {
73
+ "version": "0.10.0",
74
+ "id": "0.10.0/03_workflow-caller-context-resolved",
75
+ "title": "workflow `buildContext` receives a RESOLVED caller context",
76
+ "kind": "manual"
77
+ },
78
+ {
79
+ "version": "0.10.0",
80
+ "id": "0.10.0/04_seed-run-status-skipped",
81
+ "title": "seed run status gained `skipped`",
82
+ "kind": "manual"
83
+ },
84
+ {
85
+ "version": "0.10.0",
86
+ "id": "0.10.0/05_subscription-idle-state",
87
+ "title": "`useSubscription` gained an idle state for `skip`",
88
+ "kind": "manual"
89
+ },
90
+ {
91
+ "version": "0.11.0",
92
+ "id": "0.11.0/01_step-options-type-params",
93
+ "title": "`StepOptions` takes the context type first",
94
+ "kind": "manual"
95
+ },
96
+ {
97
+ "version": "0.12.0",
98
+ "id": "0.12.0/01_outbound-http-ssrf-guard",
99
+ "title": "Outbound HttpClient blocks internal targets by default (add `http.allowHosts` if you call one)",
100
+ "kind": "manual"
101
+ },
102
+ {
103
+ "version": "0.12.0",
104
+ "id": "0.12.0/02_api-key-revoke-tenant-scope",
105
+ "title": "apiKeyService.revoke/rotate now require the caller's tenantId",
106
+ "kind": "manual"
107
+ },
108
+ {
109
+ "version": "0.12.0",
110
+ "id": "0.12.0/03_api-key-on-behalf-of",
111
+ "title": "API keys: createdBy is provenance, onBehalfOf is ownership",
112
+ "kind": "manual"
113
+ },
114
+ {
115
+ "version": "0.12.0",
116
+ "id": "0.12.0/04_inspect-fail-closed",
117
+ "title": "The inspect surface requires VOLTRO_INSPECT_TOKEN (previously open when unset)",
118
+ "kind": "manual"
119
+ },
120
+ {
121
+ "version": "0.13.0",
122
+ "id": "0.13.0/01_bearer-gates-fail-closed",
123
+ "title": "checkBearer is fail-closed; scimPlugin refuses an empty token",
124
+ "kind": "manual"
125
+ },
126
+ {
127
+ "version": "0.13.0",
128
+ "id": "0.13.0/02_storage-grants-owner-only",
129
+ "title": "storage.share / revoke / listGrants require ownership",
130
+ "kind": "manual"
131
+ },
132
+ {
133
+ "version": "0.13.0",
134
+ "id": "0.13.0/03_apply-plan-convergence-proof",
135
+ "title": "applyPlan requires a `replan` convergence proof",
136
+ "kind": "manual"
137
+ },
138
+ {
139
+ "version": "0.14.0",
140
+ "id": "0.14.0/01_resolve-scopes-store",
141
+ "title": "auth.resolveScopes receives the app DataStore",
142
+ "kind": "manual"
143
+ },
144
+ {
145
+ "version": "0.14.0",
146
+ "id": "0.14.0/02_reactive-by-default",
147
+ "title": "Reactivity is the default; .reactive() is removed",
148
+ "kind": "transform"
149
+ },
150
+ {
151
+ "version": "0.14.0",
152
+ "id": "0.14.0/03_pages-suffix",
153
+ "title": "Pages are declared with a .page.tsx suffix",
154
+ "kind": "transform"
155
+ },
156
+ {
157
+ "version": "0.14.0",
158
+ "id": "0.14.0/04_file-taxonomy",
159
+ "title": "Web files declare their contract (*.component.tsx / *.hook.ts / *.types.ts)",
160
+ "kind": "transform"
161
+ },
162
+ {
163
+ "version": "0.14.0",
164
+ "id": "0.14.0/05_typed-tracking",
165
+ "title": "Tracking catalogues are typed against the component props",
166
+ "kind": "manual"
167
+ },
168
+ {
169
+ "version": "0.14.0",
170
+ "id": "0.14.0/06_voltro-test-skips-e2e",
171
+ "title": "`voltro test` leaves `e2e/` to `voltro e2e`",
172
+ "kind": "manual"
173
+ },
174
+ {
175
+ "version": "0.14.0",
176
+ "id": "0.14.0/07_dev-remote-file-migrations",
177
+ "title": "voltro dev refuses unattended file migrations against a remote database",
178
+ "kind": "manual"
179
+ },
180
+ {
181
+ "version": "0.15.0",
182
+ "id": "0.15.0/01_undo-mistaken-taxonomy-renames",
183
+ "title": "Undo the two file renames 0.14.0 got wrong (framework primitives, side-effect modules)",
184
+ "kind": "transform"
185
+ },
186
+ {
187
+ "version": "0.15.0",
188
+ "id": "0.15.0/02_error-tag-in-protocol",
189
+ "title": "`errorTag` is imported from @voltro/protocol",
190
+ "kind": "transform"
191
+ },
192
+ {
193
+ "version": "0.17.0",
194
+ "id": "0.17.0/01_pages-are-directories",
195
+ "title": "A directory is a route segment; its route is page.tsx",
196
+ "kind": "transform"
197
+ },
198
+ {
199
+ "version": "0.18.0",
200
+ "id": "0.18.0/01_where-like-removed",
201
+ "title": "the `'like'` where-operator is removed — pick `contains` or `startsWith`",
202
+ "kind": "manual"
203
+ },
204
+ {
205
+ "version": "0.19.0",
206
+ "id": "0.19.0/01_server-only-gates-the-boot",
207
+ "title": "`.serverOnly()` now FAILS the boot under `voltro serve` — it used to only warn",
208
+ "kind": "manual"
209
+ },
210
+ {
211
+ "version": "0.19.0",
212
+ "id": "0.19.0/02_audit-redact-input-default",
213
+ "title": "`auditPlugin` now redacts mutation input by default (`redactInput: 'all'`)",
214
+ "kind": "manual"
215
+ },
216
+ {
217
+ "version": "0.20.0",
218
+ "id": "0.20.0/01_write-recorder-port",
219
+ "title": "`WriteRecorder` takes a port (`{ append, maxOf }`) instead of a bare `append`",
220
+ "kind": "manual"
221
+ },
222
+ {
223
+ "version": "0.21.0",
224
+ "id": "0.21.0/01_maxlength-now-migrates",
225
+ "title": "`text().maxLength(n)` now migrates EXISTING columns — plan before you apply",
226
+ "kind": "manual"
227
+ },
228
+ {
229
+ "version": "0.22.0",
230
+ "id": "0.22.0/01_rename-index-op",
231
+ "title": "`MigrationOperation` gained a `rename-index` kind",
232
+ "kind": "manual"
233
+ },
234
+ {
235
+ "version": "0.22.0",
236
+ "id": "0.22.0/02_plugin-tables-voltro-prefixed",
237
+ "title": "plugin-owned tables are `_voltro_`-prefixed (the rows move themselves)",
238
+ "kind": "manual"
239
+ },
240
+ {
241
+ "version": "0.22.0",
242
+ "id": "0.22.0/03_cache-scope-gained-tenant",
243
+ "title": "`cache.scope` gained 'tenant' — an exhaustive switch over it needs a branch",
244
+ "kind": "manual"
245
+ },
246
+ {
247
+ "version": "0.22.0",
248
+ "id": "0.22.0/04_inspect-write-credential",
249
+ "title": "Mutating `/_voltro/inspect/*` endpoints need VOLTRO_INSPECT_WRITE_TOKEN",
250
+ "kind": "manual"
251
+ },
252
+ {
253
+ "version": "0.23.0",
254
+ "id": "0.23.0/01_versioning-is-opt-out",
255
+ "title": "versioningPlugin: `tables: string[]` → opt-out by default, `include` / `exclude` by value",
256
+ "kind": "manual"
257
+ },
258
+ {
259
+ "version": "0.24.0",
260
+ "id": "0.24.0/01_agent-tables-voltro-prefixed",
261
+ "title": "the two agent tables are `_voltro_`-prefixed (the rows move themselves)",
262
+ "kind": "manual"
263
+ },
264
+ {
265
+ "version": "0.25.0",
266
+ "id": "0.25.0/01_events-emit-to-publish",
267
+ "title": "`ctx.events.emit(name, data)` → `ctx.events.publish(descriptor, key, payload)`",
268
+ "kind": "manual"
269
+ },
270
+ {
271
+ "version": "0.25.0",
272
+ "id": "0.25.0/02_outgoing-event-to-define-event",
273
+ "title": "`defineOutgoingEvent` → `defineEvent({ …, webhook: { … } })`",
274
+ "kind": "transform"
275
+ },
276
+ {
277
+ "version": "0.25.0",
278
+ "id": "0.25.0/03_broadcast-channel-to-namespace",
279
+ "title": "`broadcast({ channel })` → `broadcast({ namespace })`",
280
+ "kind": "transform"
281
+ },
282
+ {
283
+ "version": "0.26.0",
284
+ "id": "0.26.0/01_event-webhook-retry-removed",
285
+ "title": "`defineEvent({ webhook: { retry } })` is gone — it never did anything",
286
+ "kind": "manual"
287
+ },
288
+ {
289
+ "version": "0.26.0",
290
+ "id": "0.26.0/02_step-retry-enforced",
291
+ "title": "workflow step `retry:` is now ENFORCED (was metadata) — drop any redundant manual retry",
292
+ "kind": "manual"
293
+ },
294
+ {
295
+ "version": "0.28.0",
296
+ "id": "0.28.0/01_notifications-resolve-subject-id-async",
297
+ "title": "`makeSubjectId` returns a Promise; `resolveSubjectId` may be async",
298
+ "kind": "manual"
299
+ },
300
+ {
301
+ "version": "0.28.0",
302
+ "id": "0.28.0/02_plugin-ref-change-event",
303
+ "title": "`PluginRefChange` is removed — the change channel's `ChangeEvent` is the one shape",
304
+ "kind": "manual"
305
+ },
306
+ {
307
+ "version": "0.28.0",
308
+ "id": "0.28.0/03_atlassian-credentials-context",
309
+ "title": "`credentialsResolver` receives `{ subject, store }` instead of a bare Subject",
310
+ "kind": "manual"
311
+ },
312
+ {
313
+ "version": "0.28.0",
314
+ "id": "0.28.0/04_audit-redacts-subject-metadata",
315
+ "title": "`auditPlugin` redacts `subject.metadata` by default",
316
+ "kind": "manual"
317
+ },
318
+ {
319
+ "version": "0.29.0",
320
+ "id": "0.29.0/01_loader-data-absence",
321
+ "title": "`useLoaderData()` throws at a level that declares no `loader`",
322
+ "kind": "manual"
323
+ },
324
+ {
325
+ "version": "0.3.0",
326
+ "id": "0.3.0/01_prod-serve-requires-build",
327
+ "title": "Production `voltro serve` requires a precompiled bundle (build before serve)",
328
+ "kind": "manual"
329
+ },
330
+ {
331
+ "version": "0.30.0",
332
+ "id": "0.30.0/01_locale-cookie-rename",
333
+ "title": "the language-preference cookie is `voltro:locale` (was `voltro:lang`)",
334
+ "kind": "transform"
335
+ },
336
+ {
337
+ "version": "0.30.0",
338
+ "id": "0.30.0/02_audit-purge-query-recheck",
339
+ "title": "re-run the credential-purge query — the 0.28.0 spelling under-reported on MySQL/MariaDB",
340
+ "kind": "manual"
341
+ },
342
+ {
343
+ "version": "0.30.0",
344
+ "id": "0.30.0/03_webhook-emit-durable-deferral",
345
+ "title": "a deferred `webhooks.emit` rides the transactional outbox; `{ immediate: true }` opts out",
346
+ "kind": "manual"
347
+ },
348
+ {
349
+ "version": "0.30.0",
350
+ "id": "0.30.0/04_workflow-run-handle-nullable-execution",
351
+ "title": "`WorkflowRunHandle.executionId` is `string | null`; `status` gains queued/dropped/skipped",
352
+ "kind": "manual"
353
+ },
354
+ {
355
+ "version": "0.31.0",
356
+ "id": "0.31.0/01_rerun-credential-scan",
357
+ "title": "re-run `voltro db scan-credentials` — on 0.30.0–0.30.2 it skipped the column credentials land in",
358
+ "kind": "manual"
359
+ },
360
+ {
361
+ "version": "0.32.0",
362
+ "id": "0.32.0/01_audit-redacts-outcome",
363
+ "title": "`auditPlugin` now redacts `outcome` by default — opt out if your trail reads it",
364
+ "kind": "manual"
365
+ },
366
+ {
367
+ "version": "0.4.0",
368
+ "id": "0.4.0/01_rbac-forbidden-to-scopeerror",
369
+ "title": "@voltro/plugin-rbac: `Forbidden` → protocol `ScopeError` (one denial tag)",
370
+ "kind": "transform"
371
+ },
372
+ {
373
+ "version": "0.4.0",
374
+ "id": "0.4.0/02_rbac-web-to-client-permissions",
375
+ "title": "@voltro/plugin-rbac/web removed — the UI gate lives in @voltro/client",
376
+ "kind": "transform"
377
+ },
378
+ {
379
+ "version": "0.5.0",
380
+ "id": "0.5.0/01_one-terminal-exactly-one",
381
+ "title": ".one() now fails on MORE than one row; NoRowFound is a declarable typed error",
382
+ "kind": "manual"
383
+ },
384
+ {
385
+ "version": "0.5.0",
386
+ "id": "0.5.0/02_typed-store-rows",
387
+ "title": "store.query() now returns the row type — casts on row fields may stop compiling",
388
+ "kind": "manual"
389
+ },
390
+ {
391
+ "version": "0.6.0",
392
+ "id": "0.6.0/01_no-dev-session-secret",
393
+ "title": "The built-in dev session secret is removed; VOLTRO_SESSION_SECRET is required",
394
+ "kind": "manual"
395
+ },
396
+ {
397
+ "version": "0.6.0",
398
+ "id": "0.6.0/02_subscription-state-union",
399
+ "title": "SubscriptionState is a discriminated union — `loading` now narrows `data`",
400
+ "kind": "manual"
401
+ },
402
+ {
403
+ "version": "0.6.0",
404
+ "id": "0.6.0/03_billing-on-stripe-sdk",
405
+ "title": "Billing moved onto the Stripe SDK; proration + dunning are now Stripe’s",
406
+ "kind": "manual"
407
+ },
408
+ {
409
+ "version": "0.6.0",
410
+ "id": "0.6.0/04_relation-key-validation",
411
+ "title": "relation key options are validated at boot; `one` now fails on more than one match",
412
+ "kind": "manual"
413
+ },
414
+ {
415
+ "version": "0.6.0",
416
+ "id": "0.6.0/05_atlassian-avatar-mode",
417
+ "title": "atlassianPlugin's `avatar` config requires an explicit `mode: 'service' | 'perUser'`",
418
+ "kind": "transform"
419
+ },
420
+ {
421
+ "version": "0.6.0",
422
+ "id": "0.6.0/06_ui-strings-connect-account",
423
+ "title": "UiStrings gained a required `connectAccount` section",
424
+ "kind": "manual"
425
+ },
426
+ {
427
+ "version": "0.7.0",
428
+ "id": "0.7.0/01_row-filter-load-failure",
429
+ "title": "A row filter that fails to load now errors instead of returning zero rows",
430
+ "kind": "manual"
431
+ },
432
+ {
433
+ "version": "0.7.0",
434
+ "id": "0.7.0/02_testing-invoke-effect-handlers",
435
+ "title": "@voltro/testing: invoke() runs Effect handlers; executor types widened",
436
+ "kind": "manual"
437
+ },
438
+ {
439
+ "version": "0.8.0",
440
+ "id": "0.8.0/01_optional-password-hash",
441
+ "title": "`UserRecord.passwordHash` is optional — password-less identities are now first-class",
442
+ "kind": "manual"
443
+ },
444
+ {
445
+ "version": "0.8.0",
446
+ "id": "0.8.0/02_ssr-streams-and-loaderdata",
447
+ "title": "`ssr` pages stream; `useLoaderData<T>()` returns `LoaderData<T>`",
448
+ "kind": "manual"
449
+ },
450
+ {
451
+ "version": "0.9.0",
452
+ "id": "0.9.0/01_ssr-layout-shell-for-spa",
453
+ "title": "A `renderMode:'spa'` page under a layout now server-renders that layout",
454
+ "kind": "manual"
455
+ }
456
+ ]
457
+ },
58
458
  "engines": {
59
459
  "node": ">=24.0.0"
60
460
  },
@@ -62,22 +462,22 @@
62
462
  "@effect/platform-node": "^0.108.0",
63
463
  "@effect/sql": "^0.52.0",
64
464
  "@effect/workflow": "^0.19.0",
65
- "@voltro/ai": "0.31.0",
66
- "@voltro/cache": "0.31.0",
67
- "@voltro/data-transfer": "0.31.0",
68
- "@voltro/database": "0.31.0",
69
- "@voltro/env": "0.31.0",
70
- "@voltro/kv": "0.31.0",
71
- "@voltro/logger": "0.31.0",
72
- "@voltro/plugin-auth": "0.31.0",
73
- "@voltro/plugin-broadcast": "0.31.0",
74
- "@voltro/plugin-mail": "0.31.0",
75
- "@voltro/plugin-storage": "0.31.0",
76
- "@voltro/plugin-webhooks": "0.31.0",
77
- "@voltro/protocol": "0.31.0",
78
- "@voltro/runtime": "0.31.0",
79
- "@voltro/serverless": "0.31.0",
80
- "@voltro/workflow": "0.31.0",
465
+ "@voltro/ai": "0.32.0",
466
+ "@voltro/cache": "0.32.0",
467
+ "@voltro/data-transfer": "0.32.0",
468
+ "@voltro/database": "0.32.0",
469
+ "@voltro/env": "0.32.0",
470
+ "@voltro/kv": "0.32.0",
471
+ "@voltro/logger": "0.32.0",
472
+ "@voltro/plugin-auth": "0.32.0",
473
+ "@voltro/plugin-broadcast": "0.32.0",
474
+ "@voltro/plugin-mail": "0.32.0",
475
+ "@voltro/plugin-storage": "0.32.0",
476
+ "@voltro/plugin-webhooks": "0.32.0",
477
+ "@voltro/protocol": "0.32.0",
478
+ "@voltro/runtime": "0.32.0",
479
+ "@voltro/serverless": "0.32.0",
480
+ "@voltro/workflow": "0.32.0",
81
481
  "chokidar": "^5.0.0",
82
482
  "ioredis": "^5.11.1",
83
483
  "tinyglobby": "^0.2.17",
@@ -652,7 +652,7 @@ each plugin's own README.
652
652
 
653
653
  | Topic | Open | Summary |
654
654
  |---|---|---|
655
- | **What's new in 0.31.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
655
+ | **What's new in 0.32.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
656
656
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
657
657
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
658
658
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -9,7 +9,7 @@ each plugin's own README.
9
9
 
10
10
  | Topic | Open | Summary |
11
11
  |---|---|---|
12
- | **What's new in 0.31.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
12
+ | **What's new in 0.32.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
13
13
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
14
14
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
15
15
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -104,6 +104,30 @@ auditPlugin({ sink: 'datastore', redactInput: (e) => pick(e) }) // field-level
104
104
 
105
105
  **Why it is not driven by `.serverOnly()` / `.sensitive()`,** which is the obvious design: those markers live on TABLE COLUMNS, and this is a mutation's INPUT. A `changePassword({ oldPassword, newPassword })` has no column to consult, so a marker-driven default would cover exactly 0% of the case it exists for — while reading, to whoever configured it, like protection. (`.sensitive()` is also the [export axis](/docs/database/sensitivity), not "unsafe to log"; treating one as the other is the category error that page warns about.) Pass a function once you know your own inputs.
106
106
 
107
+ ### `redactOutcome` — what the call RETURNED
108
+
109
+ **The default is `'all'`: `outcome.value` on success and `outcome.error` on failure are replaced by `{ __redacted: 'all' }`.** `kind`, `durationMs` and the error's `_tag` survive.
110
+
111
+ ```ts
112
+ auditPlugin({ sink: 'datastore', redactOutcome: 'none' }) // the outcome verbatim
113
+ auditPlugin({ sink: 'datastore', redactOutcome: (e) => summarise(e) }) // field-level control
114
+ ```
115
+
116
+ **This is `redactInput`'s reasoning applied to the field it structurally cannot cover.** For a credential-ISSUING call the secret is never in the input:
117
+
118
+ ```ts
119
+ apiKeys.createPersonalApiKey({ name, scopes }) // input: nothing sensitive
120
+ → { keyValue: '<the plaintext key>' } // outcome: the whole point
121
+ webhooks.create({ url, subscribedEvents }) // input: nothing sensitive
122
+ → { signingSecret: '<live secret>' } // outcome: returned once, by design
123
+ ```
124
+
125
+ The option that existed covers the field those calls leave empty. Found by a team running `voltro db scan-credentials` on the first release where it read the json blobs: nine rows of `_voltro_audit_log.outcome` matched, four of them `webhooks.create` carrying a live 64-character signing secret in full — with `redactInput: 'all'` and `redactSubject: 'metadata'` both already on.
126
+
127
+ **The error's `_tag` survives on purpose.** A trail that records "something failed" without saying what is not a trail, and a tag is a schema-declared discriminant that structurally cannot be a secret. A `record` predicate still sees the live outcome, so a filter keyed on what a call returned keeps working — redaction applies to what is *stored*.
128
+
129
+ > **A FUNCTION sink gets neither the table nor the retention sweep.** Both are gated on `sink` being the literal `'datastore'`, so a function that redacts and then delegates to `dataStoreAuditSink` still writes rows on a database where `_voltro_audit_log` already exists — while creating the table nowhere and arming the TTL nowhere. It works where you tested it and fails on the next fresh database. If you want the durable trail with different redaction, use `sink: 'datastore'` plus the three `redact*` options: those compose, the sink does not.
130
+
107
131
  ### `redactSubject` — what of the CALLER is kept
108
132
 
109
133
  **The default is `'metadata'`: `subject.metadata` is replaced by `{ __redacted: 'all' }`.** `type`, `id`, `tenantId` and `scopes` survive, which is everything the trail is actually read for.
@@ -106,6 +106,15 @@ By default it scans every column that can physically HOLD one:
106
106
 
107
107
  `input` and `outcome` are a procedure's arguments and result — the likeliest place of all for a token to arrive by accident, since nobody puts a credential on a subject on purpose and plenty of code passes one as an argument. `_voltro_row_history.data` is a full-row snapshot, so a credential column on *any* of your tables is in there, and it outlives deleting the source row.
108
108
 
109
+ Every hit line names the needles that matched, with the number of rows each appears in:
110
+
111
+ ```
112
+ ✗ _voltro_audit_log.outcome — 69 of 149 row(s) match a credential-shaped key
113
+ matched (rows per needle, may overlap): token (61), secret (12)
114
+ ```
115
+
116
+ **Those counts overlap and do not sum to the hit count** — a row holding both a token and a secret is counted by both needles. Without the breakdown the advice underneath (*purge them AND rotate the credentials*) is neither executable — purge what, rotate which — nor refutable: a column mentioning the word `token` in prose reads identically to one holding a live one.
117
+
109
118
  > **If you ran this on 0.30.0, 0.30.1 or 0.30.2, run it again.** Those releases scanned `subjectId` on both tables — a flat opaque id that cannot hold a credential — and none of the blob columns above. The command ran cleanly, printed a scanned count beside a hit count, and exited `0` having never looked where credentials are. It came from fixing a crash: the default column had been the literal `subject` for every table, `_voltro_row_history` has no such column, and the fix replaced the name on *both* tables instead of the one that was wrong. A clean answer from a scan that looked in the wrong place is worse than the crash it replaced. `voltro update` prints this as a manual step on the way to 0.31.0.
110
119
 
111
120
  **It is a command and not a documented query on purpose.** The same check once shipped as SQL you were asked to run yourself, in its postgres spelling (`subject::text ILIKE '%token%'`). On MySQL/MariaDB the natural translation is a bare `LIKE` — and against the `utf8mb4_bin` collation the migrator emits for a `json()` column, `LIKE` is case-**sensitive**. So `'%token%'` does not match `jiraToken`, and almost every JSON key that carries a credential is camelCase. A team ran the translated query over 141 rows, got `0`, and nearly reported themselves clean; 117 of those rows held a working credential. Every dialect now casts to its own text type before lowering, in code you do not have to translate.