@voltro/plugin-presence 0.84.0 → 0.86.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 +1215 -0
- package/THIRD-PARTY-NOTICES.md +5 -89
- package/dist/errors.d.ts +5 -8
- package/dist/index.d.ts +6 -8
- package/dist/index.js +69 -246
- package/dist/rpc.d.ts +18 -21
- package/dist/rpc.js +4 -10
- package/dist/tracker.js +134 -0
- package/dist/trackerBus.js +63 -0
- package/dist/web.js +1 -7
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,1221 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.86.0] — 2026-10-21
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING
|
|
45
|
+
|
|
46
|
+
- **Licensing refresh uses authenticated bounded JSON snapshots** — `@voltro/plugin-licensing`, `@voltro/cli`
|
|
47
|
+
|
|
48
|
+
License snapshot refresh accepts JSON containing a signed `token`, matching the Cloud license endpoint. Configure server-only `snapshotHeaders` for authenticated endpoints. Refresh refuses redirects, limits responses to 64 KiB and times out after ten seconds. Refusals retain the last verified snapshot under its existing expiry and grace rules and log only a fixed reason and optional HTTP status.
|
|
49
|
+
|
|
50
|
+
Custom endpoints returning a raw token must return the JSON envelope instead. The update command provides the endpoint and credential configuration steps. Unsigned response metadata never changes installed entitlements.
|
|
51
|
+
|
|
52
|
+
**`voltro update` carries you across this** — codemod `0.86.0/08_license-snapshot-envelope`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
53
|
+
- **Index draft and revision references with content writes** — `cms`, `cli`
|
|
54
|
+
|
|
55
|
+
Draft and history reverse-reference reads now query scoped stored edges, maintained in the content transaction. Apply cmsReferencesTable and backfill existing drafts and revisions with rebuildReferenceIndex before using reverse reads or replacement plans; the upgrade note covers tenant-scoped migration and rollback validation.
|
|
56
|
+
|
|
57
|
+
The references table now also stores draft and revision edges, told apart by a `stage` column. A query of your own on `CMS_REFERENCES_TABLE` that means live uses — a usage count, which pages show an asset, whether a file may become public — must filter `stage: 'published'`; otherwise a draft counts as a use, or opens a private file.
|
|
58
|
+
|
|
59
|
+
**`voltro update` carries you across this** — codemod `0.86.0/01_cms-reference-index-backfill`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
60
|
+
- **Reject environment values that would disappear from browser snapshots** — `@voltro/env`, `@voltro/cli`
|
|
61
|
+
|
|
62
|
+
Decoded environment values that JSON would omit, coerce or fail to encode now refuse boot by variable name. Defaults and explicit values use the field's existing decoder, bound to its own Effect instance.
|
|
63
|
+
|
|
64
|
+
Migration: ordinary `envVar` declarations need no changes. Custom `EnvFieldSpec` / `AnyEnvFieldSpec` decoders must return JSON-preserving values through plain `{ ok: true, value }` or `{ ok: false, message }` outcomes. Convert custom dates to ISO strings, and replace symbols, functions, bigint, non-finite numbers or cyclic values with a declared JSON representation. Keep decoding beside the schema using the same Effect instance.
|
|
65
|
+
|
|
66
|
+
**`voltro update` carries you across this** — codemod `0.86.0/05_environment-field-owned-decoder`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
67
|
+
- **Accept numeric store keys and normalize native numeric IDs** — `@voltro/database`, `@voltro/runtime`, `@voltro/testing`, `@voltro/voltro`
|
|
68
|
+
|
|
69
|
+
Keyed update, delete, hardDelete and patchJson accept numeric IDs directly, including tenant-scoped and transactional writes. SQL stores normalize numeric ID and numeric-reference results to the declared number type, matching the in-memory test store; text IDs remain strings. Numeric identities outside JavaScript’s safe integer range are refused rather than rounded.
|
|
70
|
+
|
|
71
|
+
Custom adapters consuming `RowKey` must handle numbers alongside strings before treating remaining keys as records, or use `rowKeyColumns`, `rowKeyIdentity` and `isCompositeRowKey`. Review string-ID workarounds and numeric identity ranges with the upgrade note.
|
|
72
|
+
|
|
73
|
+
**`voltro update` carries you across this** — codemod `0.86.0/04_numeric-store-keys`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
74
|
+
- **Registered session rotation preserves device revocation** — `@voltro/plugin-auth`
|
|
75
|
+
|
|
76
|
+
`rotateRegisteredSession` atomically replaces a current credential while retaining inactive device aliases. Signing out through any family member also ends its successor; other devices remain signed in. Failed persistence rolls back rotation. `findSession` and `listSessions` exclude rotated aliases, and revocation counts current credentials. PostgreSQL session persistence failures now fail issuance.
|
|
77
|
+
|
|
78
|
+
The sessions schema adds nullable `familyId` and `rotatedAt` columns and a family index. Review and apply the declared schema before restarting upgraded auth replicas. Applications declaring their own sessions table must mirror this shape. Registry touch/revocation writes now own a transaction; callers already inside a mutation or explicit transaction must pass `{ transaction: 'existing' }` to `dataStoreSessionRegistry`. Rotation accepts the same ownership option. The upgrade codemod reports the required schema and source review steps.
|
|
79
|
+
|
|
80
|
+
**`voltro update` carries you across this** — codemod `0.86.0/04_registered-session-device-rotation`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
81
|
+
- **Store release artifact byte counts beyond the 32-bit range** — `@voltro/plugin-releases`, `@voltro/cli`
|
|
82
|
+
|
|
83
|
+
Release artifact metadata now uses exact BIGINT storage while upload inputs and updater feeds retain numeric sizes up to Number.MAX_SAFE_INTEGER. Direct releaseArtifactsTable.size and ReleaseArtifactRow.size access now uses decimal strings; use BigInt(row.size) for exact arithmetic and supply strings in direct table writes. Run voltro db deploy before production startup to apply the safe integer widening; existing byte counts are retained. The update command reports direct table consumers for review.
|
|
84
|
+
|
|
85
|
+
**`voltro update` carries you across this** — codemod `0.86.0/09_release-artifact-byte-storage`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
86
|
+
- **Recover release file publishing without duplicate files or artifacts** — `@voltro/plugin-releases`, `@voltro/cli`
|
|
87
|
+
|
|
88
|
+
File publishers now preserve one operation identity through preparation, durable Storage tickets and release metadata. Retries, lost successful responses and renewed credentials return the original file and artifact without advancing the target or appending another event. Conflicting immutable intent is refused; current project authorization is checked on every attempt.
|
|
89
|
+
|
|
90
|
+
Deploy the updated API before the file CLI. File publication requires bound durable Storage operations, supplied by native default reference/grant stores. Custom publish facades must forward operationId and namespace Storage identity by canonical project/release; custom stores without that capability refuse file publishing. The registered manual upgrade explains the required review without changing authorization rules.
|
|
91
|
+
|
|
92
|
+
Metadata-only callers can supply operationId, including --operation-id in the CLI. This change does not reserve authority over a transfer, infer identities for prior unidentified uploads or automatically clean orphan provider objects.
|
|
93
|
+
|
|
94
|
+
**`voltro update` carries you across this** — codemod `0.86.0/10_release-upload-recovery`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
95
|
+
- **Required audit admission retains unknown invocation outcomes** — `@voltro/plugin-audit`, `@voltro/cli`
|
|
96
|
+
|
|
97
|
+
`auditPlugin({ invocationCapture: 'required' })` durably records a redacted action or workflow-start admission before its effect may run. It requires the datastore sink, both outcome outbox policies and `record: 'all'`. Failed or uncertain admission prevents execution. An interrupted admitted call retains an explicit unknown result; terminal outcomes correlate through `outcome.invocationId` without overwriting admission evidence. `AuditEvent.outcome` now includes `kind: 'unknown'`: update exhaustive sinks, redactors and views to distinguish it from success and error. Unknown does not establish rollback or permit automatic replay. Mutation, query and HTTP capture policies retain their own contracts.
|
|
98
|
+
|
|
99
|
+
`auditInvocationQuery()` filters and paginates logical invocations without deleting raw admissions; `auditInvocationIdentity()` supplies their stable display identity. Apply authorization and tenant filters using the `audit.` alias and retain raw row ordering keys for cursors. The nullable indexed lookup is derived from the protected outcome and checked during receipt replay and chain verification. `AuditLogTable` now requires full `SchemaTable` metadata; replace hand-built partial descriptors with the supplied `auditLogTable`.
|
|
100
|
+
|
|
101
|
+
**`voltro update` carries you across this** — codemod `0.86.0/05_audit-capture-contract-review`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
102
|
+
- **Physical storage namespaces preserve independent cleanup and shared bytes** — `@voltro/plugin-storage`, `@voltro/cli`
|
|
103
|
+
|
|
104
|
+
Separate reference rights from physical object ownership. Provider namespaces isolate operation replay and independent generations while sharing counts for actual shared bytes. Existing metadata requires an explicit stopped-replica after-schema assignment that preserves reference IDs, keys, grants, claims, quota and raw operation identities. Custom providers supply `objectNamespace`; direct DataStore metadata factories take an explicit storage scope. Preserve filesystem namespace markers with their volumes and finish returned retired-key cleanup through the matching service.
|
|
105
|
+
|
|
106
|
+
**`voltro update` carries you across this** — codemod `0.86.0/06_storage-object-namespaces`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
107
|
+
- **Storage replica inspection identifies and combines reference catalogues** — `@voltro/plugin-storage`, `@voltro/protocol`, `@voltro/devtools-ui`, `@voltro/cli`
|
|
108
|
+
|
|
109
|
+
`RefStore` requires a stable `catalogueId` effect. Default stores retain a schema-managed catalogue identity; direct DataStore adapters initialize it after schema reconciliation, and custom wrappers forward their underlying identity. Combined replica reads deduplicate shared catalogue rows and counters, preserve independent origins, and retain missing-replica evidence. Shared dashboards distinguish equal reference IDs from different catalogues and require one selected replica for file operations. Apply the normal schema before production boot and follow the upgrade note for custom stores and hosts.
|
|
110
|
+
|
|
111
|
+
**`voltro update` carries you across this** — codemod `0.86.0/07_storage-reference-catalogue-identity`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
112
|
+
- **Upload access policies reject oversized rule and principal arrays** — `@voltro/protocol`, `@voltro/plugin-storage`
|
|
113
|
+
|
|
114
|
+
Self-service upload policies accept at most 32 rules and 64 entries per rule's `roles`, `groups` and `scopes`. The same limits apply to server-approved upload policies and all four signed upload-ticket verifiers, before provider work. Previously accepted oversized policies are refused rather than truncated. Review upload defaults, authorization functions and custom ticket issuers before upgrading; use per-object grants for larger principal sets and restart outstanding upload sessions whose signed policies exceed the bounds. Existing stored references and grants are unchanged.
|
|
115
|
+
|
|
116
|
+
**`voltro update` carries you across this** — codemod `0.86.0/03_storage-policy-bounds`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
117
|
+
- **Bind upload tickets to shared metadata and physical storage** — `@voltro/plugin-storage`, `@voltro/cli`
|
|
118
|
+
|
|
119
|
+
All four upload-ticket families use version 2 and require a destination scope from the selected Storage service. A second replica with the same reference catalogue and physical bytes can accept the ticket. A different catalogue or physical namespace refuses it before provider work. Inspect tickets retain a separate signed issuer routing hint for the binary relay.
|
|
120
|
+
|
|
121
|
+
Renew outstanding version-1 upload tickets after coordinating issuers and receivers. Custom signers pass writeScope from the selected service's uploadTicketScope(); custom StorageServiceShape implementations provide that method. Preserve existing policy, attribution, limits and durable operation identity. Read grants and stored objects are unchanged. The registered manual upgrade covers custom issuers, metadata/provider identity and unfinished sessions; this does not add early revocation or automatic staged-object cleanup.
|
|
122
|
+
|
|
123
|
+
**`voltro update` carries you across this** — codemod `0.86.0/11_storage-upload-ticket-scope`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
124
|
+
- **Audit mutations can capture redacted intents transactionally** — `@voltro/plugin-audit`, `@voltro/protocol`, `@voltro/cli`
|
|
125
|
+
|
|
126
|
+
Add opt-in `mutationCapture: 'transactional-outbox'` for successful registered mutations with the datastore sink. Capture a redacted, actor-bound portable audit intent on the actual business transaction and deliver through the shared outbox handler using retained receipt identity. Failed required capture rolls back business writes; delivery retry/restart cannot rewrite a captured actor or duplicate a retained receipt. The default and non-success/action/workflow/HTTP recording remain best effort.
|
|
127
|
+
|
|
128
|
+
AuditEvent actor snapshots can be null as well as undefined; custom consumers must use a null-safe guard. PluginPermission now includes `outbox:handle`, so exhaustive maps and switches need deliberate handling. Existing one-argument `dataStoreAuditSink` calls remain valid; the optional receipt identity is for captured delivery retries. The upgrade codemod identifies affected imports and reports the review steps without automatically changing actor or permission policy.
|
|
129
|
+
|
|
130
|
+
**`voltro update` carries you across this** — codemod `0.86.0/05_audit-capture-contract-review`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.86.0).
|
|
131
|
+
|
|
132
|
+
### Added
|
|
133
|
+
|
|
134
|
+
- **Project audit actor labels through a configured policy** — `@voltro/plugin-audit`
|
|
135
|
+
|
|
136
|
+
Add an optional `redactActor` policy to the audit plugin. Project resolved display-name and email labels before best-effort writes or required outbox capture while preserving immutable actor identity. Freeze the policy decision in each intent and reuse it during delivery and replay. Invalid required capture fails before commit; refusal admission preserves the original error.
|
|
137
|
+
|
|
138
|
+
Cloud activity capture and customer/operator history projections omit recognized credential-bearing actor labels and retain a safe immutable identity fallback. Historical stored bytes and hash links remain unchanged. The default plugin policy and row-history behavior are unchanged.
|
|
139
|
+
- **Reuse bounded interruptible HTTP ingestion** — `@voltro/plugin-storage`
|
|
140
|
+
|
|
141
|
+
`fetchIngestBody` exposes the same bounded HTTP byte reader used by Storage URL ingestion, including source checks, redirect limits and interruption. It reads bytes only; callers must apply their own authorization and persist through the complete Storage pipeline. It is not an RSS or zero-copy guarantee.
|
|
142
|
+
- **Caller-executed tools pause a mixed server tool loop** — `@voltro/ai`
|
|
143
|
+
|
|
144
|
+
Declare `execution: 'client'` on a tool to emit a marked pending call without a server executor or synthetic result. Server-only steps keep looping; send actual assistant/tool results in the next request to continue.
|
|
145
|
+
- **API builds include a readonly compiled migration planner** — `@voltro/cli`
|
|
146
|
+
|
|
147
|
+
API builds now emit a separate readonly migration-plan executable that runs with plain Node after runtime pruning. It uses the built application's module and SQL driver registries, preserves complete JSON plans and blocked exit-code semantics, and starts no application lifecycle or DDL execution. Schema-import failures refuse planning instead of emitting partial plans. Direct API and planner bundle entrypoints also correctly recognize execution through symbolic links.
|
|
148
|
+
- **Successful actions can retain audit outcomes for outbox delivery** — `@voltro/plugin-audit`
|
|
149
|
+
|
|
150
|
+
Add `successCapture: 'outbox'` for completed registered actions and workflow-start calls with the datastore audit sink. Captured outcomes retain their redacted payload and actor snapshot through sink outages, delivery retries and process restarts. Successful mutations keep their transaction capture without duplicate events. Failed outcome admission preserves the completed business result and reports a bounded diagnostic; external side effects and pre-admission process failures remain outside this delivery guarantee.
|
|
151
|
+
- **The devtools capsule shows what a page load waits for under `voltro dev`** — `@voltro/cli`, `@voltro/web`, `@voltro/devtools`, `@voltro/client`, `@voltro/protocol`
|
|
152
|
+
|
|
153
|
+
A full page load used to show nothing in the capsule while the dev server compiled and rendered the next page; only the browser's tab spinner moved. `voltro dev` now reports each page request to the tab that is waiting for it over the HMR socket, and the capsule shows `Loading /orders`, `Compiling /orders`, `Rendering /orders` or `Redirecting to /login` until the new page replaces the old one. If the navigation never replaces the page (a `204`, a download, or a stopped load), the status clears when the dev server reports how the request ended. A client-side navigation waiting for a page's code now shows `Loading page…`, or `Compiling page` while Vite compiles it. Previously it showed nothing. A lost dev server shows `Dev server offline`. Other tabs of the same app are not affected. A page navigation takes precedence over every other status: `DevStatus` gained an optional `priority`, and `selectDevStatus()` picks the status a single-status surface shows. A production build contains none of this.
|
|
154
|
+
- **Durable audit refusal delivery** — `@voltro/plugin-audit`, `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`
|
|
155
|
+
|
|
156
|
+
Add opt-in outbox delivery for failed registered mutations, actions and workflow starts after the business transaction settles. Capture redacted actor-bound intents and replay with stable receipt identity while preserving the original invocation error. Queries, transport failures, successful actions and unavailable-outbox admission remain outside this guarantee.
|
|
157
|
+
- **Declare PostgreSQL GIN trigram and JSON operator classes** — `@voltro/database`
|
|
158
|
+
|
|
159
|
+
`kindOptions.gin.opclass` declares an operator class for a single-column GIN index. Trigram indexes install pg_trgm before DDL. Schema creation, migration planning, catalog adoption, drift fingerprints and concurrent index creation retain the class; other dialects keep their documented GIN fallback.
|
|
160
|
+
- **A page's meta reads its locale's catalog as messages** — `@voltro/web`, `@voltro/i18n`, `@voltro/cli`
|
|
161
|
+
|
|
162
|
+
A page's `meta` function now receives `messages`: the message catalog of its locale, next to `locale`. A translated title reads `messages['meta.home.title']` instead of importing the catalogs into the page — a value import of every catalog put every language into that page's chunk, past the per-locale split. With `locales` in `app.config.ts`, the server, the prerender and the static shells hand `meta` the catalog of the render's locale. In the browser the document's own catalog is loaded before the first render, and a client navigation into another URL locale loads that locale's catalog and resolves `meta` again; until it arrives, the previous title stays. Without the i18n wiring `messages` is empty. Type it with the keys of your base catalog: `import type en from './en'` and `Record<keyof typeof en, string>`. The templates read their titles this way now.
|
|
163
|
+
- **Read native URL parameters without schema startup** — `@voltro/web`
|
|
164
|
+
|
|
165
|
+
Add `useUrlSearchParams()` for native, SSR-aware URL reads without schema decoding. Existing typed readers and setters retain their behavior. Router contexts and the request/router precedence are shared; schema-free link encoding no longer imports the decoder.
|
|
166
|
+
- **Outbox handlers publish declared events in the queued tenant** — `@voltro/cli`, `@voltro/runtime`
|
|
167
|
+
|
|
168
|
+
Effect outbox handlers can yield OutboxEventPublisher to publish declared events through the usual durable workflow and webhook audiences. Publication failure retries before delivery acknowledgment; the worker starts after its environment is ready. Queued user IDs do not restore request authority. Delivery remains at-least-once; use outboxId to deduplicate downstream effects.
|
|
169
|
+
- **Plugin outbox handlers use the shared delivery worker** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`
|
|
170
|
+
|
|
171
|
+
Server plugins can contribute `outboxHandlers` with the `outbox:handle` permission. Dev and compiled serve share handler registration, definition validation, collision refusal, inventory and the existing delivery worker. Runtime continues to export the canonical handler types. Retry budgets must be positive safe integers. Unknown effects remain pending without claim/release churn and cannot starve registered effects in the eligible batch. This contribution does not itself provide transactional audit capture.
|
|
172
|
+
- **Reuse plugin boot validation in server embeddings** — `@voltro/cli`
|
|
173
|
+
|
|
174
|
+
The server-only `@voltro/cli/startup` export provides the same permission audit and plugin lifecycle/configuration orchestration used by normal CLI starts. Embeddings can reuse these rules instead of maintaining a separate boot implementation. This does not isolate untrusted code or validate an entire application.
|
|
175
|
+
- **Explicit public documentation MCP providers** — `@voltro/mcp`, `@voltro/cli`, `@voltro/web`
|
|
176
|
+
|
|
177
|
+
Add explicit MCP content providers and a bounded stateless HTTP adapter, sharing the existing lifecycle and transport without exposing app inspection credentials. Ship bilingual public-content and WebMCP guidance in customer agent docs.
|
|
178
|
+
|
|
179
|
+
WebMCP execution tolerates early trial browsers without per-call options and always honors registration lifetime cancellation as well as browser cancellation.
|
|
180
|
+
|
|
181
|
+
Offer the existing WebMCP helpers through `@voltro/web/webmcp` so progressive browser integrations avoid importing the router, RPC client and i18n barrel.
|
|
182
|
+
|
|
183
|
+
Include islands imported by ancestor layouts in each page’s browser entry, preserving client detection and provider-boundary guards.
|
|
184
|
+
|
|
185
|
+
`routeHttp` keeps its name and fields: `RouteHttpOptions` now extends a shared base and the function also accepts the new `ProviderRouteHttpOptions`, so every existing call still compiles (the golden lines change, nothing is removed or narrowed).
|
|
186
|
+
- **Run trusted release management in the caller transaction** — `@voltro/plugin-releases`
|
|
187
|
+
|
|
188
|
+
Release plugin instances expose validated server-side management Effects that join an explicit caller transaction, so application authorization and native release metadata writes can commit or roll back together.
|
|
189
|
+
- **Bind authenticated requests to independently selected tenants** — `@voltro/protocol`
|
|
190
|
+
|
|
191
|
+
`composeAuthStrategies` accepts a `requestedTenant` source alongside fresh `memberships`. Explicit targets are authorized before scopes and guards, take precedence over stored selection without changing it, and fail closed when invalid or no longer permitted. Live authority revalidation repeats the same membership check. Missing hints retain existing behavior. This is a low-level composer primitive; it does not add an app-config option or automatically wire client, SSR, or loader workspace context.
|
|
192
|
+
- **Configure request-local tenant binding in API auth** — `@voltro/cli`
|
|
193
|
+
|
|
194
|
+
API applications can configure `auth.requestedTenant` alongside fresh `auth.memberships`. Development and compiled production starts pass the whole configuration through the shared auth builder. Explicit targets are authorized before scopes and guards without changing persistent selection. No default header convention is introduced; clients, SSR and loaders must supply their workspace context consistently.
|
|
195
|
+
- **Require explicit resource verdicts for scope guards** — `@voltro/protocol`, `@voltro/devtools-ui`
|
|
196
|
+
|
|
197
|
+
Scope guards can declare `resourceOnly: true` to require the registered resource resolver's verdict even when the subject holds the scope globally or has `admin:full`. Missing resources or resolvers, invalid selectors, and failed resolver Effects refuse access. Existing scope guards keep their global-grant behavior.
|
|
198
|
+
|
|
199
|
+
Inspection panels recognize canonical scope refusals without an HTTP status and show the localized access-recovery message instead of exposing a structured error as text.
|
|
200
|
+
- **REST routes declare CORS, bearer access and quotas** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`, `@voltro/plugin-ratelimit`
|
|
201
|
+
|
|
202
|
+
REST descriptors accept `cors`, `credentials: 'bearer'` and `rateLimit`. Matching responses include CORS through framework refusals and OPTIONS; bearer routes discard cookies and bypass only their own method's origin guard. Quotas use the mounted rate-limit plugin and fail to mount when it is absent.
|
|
203
|
+
- **Pending UI submissions survive editor destination changes** — `@voltro/devtools-ui`
|
|
204
|
+
|
|
205
|
+
Expose `useScopedSubmissions` to retain a synchronous submission lease above editors that remount for another destination. Returning to the same destination retains its pending guard; unrelated destinations remain usable, and an old release cannot unlock a newer operation. The primitive guards mounted UI submission and does not replace server authorization, durable receipts or uncertain-outcome recovery.
|
|
206
|
+
- **Control the tenant mixin index when a composite covers reads** — `@voltro/plugin-multitenancy`
|
|
207
|
+
|
|
208
|
+
`tenant({ index: false })` omits the automatic tenantId index when an application declares a covering composite B-tree. The default remains indexed; tenant isolation and audit stamping are unchanged.
|
|
209
|
+
- **Required mutation hooks share the transaction outbox facade** — `@voltro/protocol`, `@voltro/runtime`
|
|
210
|
+
|
|
211
|
+
Required mutation contributions receive the same enqueue-only outbox facade as the executor for the current transaction attempt, when handlers are registered. Business data and intent commit or roll back together; delivery nudges survive only successful commits. Dev and compiled serve also supply runner start and elapsed timing. Existing Runtime enqueue option exports retain their canonical Protocol contract. This capability alone does not adopt required audit capture or make actions/workflows transactional.
|
|
212
|
+
- **Plugins can store required records before a mutation commits** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`
|
|
213
|
+
|
|
214
|
+
Plugins can declare `beforeMutationCommit` to write required records or durable intents through the actual mutation transaction. A failure rolls back business data and participant writes together; contention retries replay both. Development and compiled production starts share the hook, with explicit interception and write permissions. Existing RPC interceptors and best-effort audit logging retain their behavior. External I/O belongs after commit and needs durable, idempotent delivery.
|
|
215
|
+
- **Install portable UI source into React library packages** — `@voltro/cli`
|
|
216
|
+
|
|
217
|
+
`voltro ui init/add/status/update --package <path>` installs and tracks registry source in a named React library. Local imports are relative to their installed files, preserving full license headers and the ordinary protection for edited source. Select either a web app with `--app` or a library with `--package`.
|
|
218
|
+
- **Initialize new web apps with Voltro UI and conditional agent guides** — `@voltro/cli`
|
|
219
|
+
|
|
220
|
+
`voltro create-project --ui-library voltro-ui` initializes the selected web template with Signal, local fonts, component configuration and a UI source lock before the normal package installation. `--ui-registry` selects an alternative HTTPS or loopback registry. Agent guides expose the UI source reference only for workspaces and web apps with a valid UI lock.
|
|
221
|
+
- **Install and update UI source while preserving local edits** — `@voltro/cli`
|
|
222
|
+
|
|
223
|
+
`voltro ui init`, `add`, `status` and `update` manage Base UI components in Voltro web apps with exact registry versions and a checked-in file-hash lock. Updates preserve edited or deliberately deleted files, place conflicting new source beside the original, and report removed items with their supplied attribution link. Initialization installs local theme and font assets; status can adopt byte-identical source installed through the shadcn CLI.
|
|
224
|
+
- **Report UI source updates after framework upgrades** — `@voltro/cli`
|
|
225
|
+
|
|
226
|
+
Framework updates report different or pending versions and removed items from existing UI source locks, with a separate `voltro ui status` review command. Registry failures remain explicit advisory failures; copied source and UI locks are never changed by the framework update.
|
|
227
|
+
- **Install Theme Studio share links in fresh web apps** — `@voltro/cli`
|
|
228
|
+
|
|
229
|
+
`voltro ui init --theme <link>` installs the exact theme encoded in a Theme Studio share URL with full MIT attribution. The lock retains its source URL and file hashes, so status and update keep that theme identity and preserve edited or deliberately deleted files. Theme URLs use HTTPS or loopback HTTP, refuse redirects and retain the existing response-size and timeout limits.
|
|
230
|
+
- **A shared web optimization skill for Codex and Claude Code** — `@voltro/cli`
|
|
231
|
+
|
|
232
|
+
New projects receive `voltro-optimize`: a repeatable Lighthouse and browser pass covering performance, accessibility, best practices, SEO and agentic browsing, with targets by website type, intentional noindex exceptions and before/after evidence. The skill library keeps canonical Claude entrypoints and seeds Codex references to the same instructions. Agent guides include the website quality targets; existing projects can add missing skills with `voltro agents-md` without overwriting their own files.
|
|
233
|
+
- **Recover stopped workflow starts with proven non-dispatch** — `@voltro/workflow`, `@voltro/cli`, `@voltro/voltro`
|
|
234
|
+
|
|
235
|
+
Add read-only `voltro workflows flow submissions` and `flow recover <submissionId>` previews, with explicit `--commit --attempts N --stopped-at N` recovery for completed transfers that prove engine dispatch never began. Recovery retains the original input, reservation and lifetime attempt count, grants a fresh configured retry budget, and preserves ambiguous acceptance. Reusing the same stop token safely reconciles a lost response without reopening a later stop. Commit requires one selected API target and the separate Inspect write credential. Low-level failure and rejection helpers add an optional observed recovery epoch; pass the submission's `retry.recovery?.attempts ?? 0` so delayed failures cannot consume the new budget.
|
|
236
|
+
|
|
237
|
+
### Changed
|
|
238
|
+
|
|
239
|
+
- **A page downloads the message catalog of its language only** — `@voltro/cli`
|
|
240
|
+
|
|
241
|
+
With `locales` and `src/locales/<code>.ts`, the generated entry imported every catalog statically, so each visitor downloaded every language in the app's entry. `voltro build` now gives every catalog its own chunk: the entry loads the one of the language the server rendered before its first render — nothing renders untranslated and hydration matches — and the server announces it as `modulepreload` in that document, so it downloads with the entry. Measured on an app with English and German catalogs: about 30 KB gzip less per first load. `voltro dev` keeps the catalogs static, so an edited catalog still hot-updates.
|
|
242
|
+
- **Reject unsupported data flags before loading command execution** — `@voltro/cli`
|
|
243
|
+
|
|
244
|
+
Invalid `voltro data` flags now return their existing message and exit code sooner, before loading database, storage and transfer execution modules. Valid commands retain the same validation and execution path.
|
|
245
|
+
- **Pages that read no data hydrate without loading the rpc runtime** — `@voltro/web`, `@voltro/client`, `@voltro/cli`
|
|
246
|
+
|
|
247
|
+
An app with `apis` and routes no longer carries its rpc runtime — the client, the WebSocket transport, the subscription cache, Effect and the api schemas — in its first load. The generated entry calls the new `mountWithDeferredApis`; `voltro build` reads each route's page, its layouts and the files they import for a data hook and loads the runtime together with the routes that use one. Every route's HTML now also announces its page chunk (and, for a route that reads data, the runtime) as `modulepreload`, so both download with the entry instead of after it. Measured under a throttled connection on an app with one api: a page without data hydrated 1674 → 975 ms with 145 → 78 KB of JavaScript before it; a page with one subscription hydrated 1692 → 1456 ms and showed its first data 2001 → 1805 ms. The runtime still arrives on every page — after the first commit under `web.api.connect: 'eager'`, when the page is idle under `'lazy'` — so connections open as before. A data hook the build cannot see waits for the runtime instead of failing. `FrameworkRuntimes` gains an optional `ready()`; `ApiRuntimeController`, `createApiRuntimeController`, `deferredApiRuntime`, `builtApiRuntime` and `toMountedApis` are new exports the generated entry uses.
|
|
248
|
+
- **Apps without apis load about 40% less JavaScript before they are interactive** — `@voltro/web`, `@voltro/client`, `@voltro/cli`
|
|
249
|
+
|
|
250
|
+
An app whose `app.config.ts` declares no `apis` no longer ships the rpc runtime. The generated entry now calls the new `mountWithoutApis(App, options)` instead of `mount(App, { apis: {} })`: same platform, hydration, islands, store seeds, service worker and Devtools, with empty runtime contexts and no supervisor, WebSocket transport or subscription cache. The router reads the browser half of a loader's `ctx.query` from the runtimes the boot provides (`FrameworkRuntimesProvider`'s new optional `queryOnce`) instead of importing it, so it no longer pulls the Effect runtime into every entry chunk; a provider mounted by hand without `queryOnce` leaves `ctx.query` undefined in the browser, as for an app without apis, and the development console bridge, server log relay and trace overlay load only in a development build. Measured on an app with pages, layouts, i18n and client stores but no apis: first load 179.7 → 105.8 KB gzip; with one form page added, 206.1 → 106.2 KB gzip. Apps with apis lose about 2 KB gzip. `window.voltroDiagnostics`, the support control for api sockets, now exists only in apps that mount an api.
|
|
251
|
+
- **A route group's layout loads only with the routes under it** — `@voltro/cli`, `@voltro/web`
|
|
252
|
+
|
|
253
|
+
The generated route table imported every layout of every route group statically, so a public page downloaded the app area's layout and everything it imports — its data hooks, schemas and rpc helpers included. `voltro build` now loads a layout in parallel with the page chunk of a route under it, and the route's HTML announces both as `modulepreload`. The root layout and the layouts above a `loading.tsx` (whose skeleton renders inside them) stay in the entry. A not-found inside such a group loads the group's layouts before it renders; `NotFoundDescriptor` gains an optional `load()` for that, and `preloadRouteModule` takes the not-founds so an unmatched URL hydrates inside its layouts. A route's initial stylesheets now also include its nested layouts' CSS, which was looked up under the wrong path. Measured on an app with a public site and an editor area: its public pages' first load went from 162 to 108 KB gzip, together with this release's fix for `errorMessage`. `voltro dev` keeps every layout static, so layout hot updates are unchanged.
|
|
254
|
+
- **Pages ship only the package code they reach** — `@voltro/client`, `@voltro/web`, `@voltro/protocol`, `@voltro/i18n`, `@voltro/ui`
|
|
255
|
+
|
|
256
|
+
Every published `@voltro/*` package now keeps one file per source module in `dist/` instead of merging its modules into a few shared files. A bundler assigns whole files to chunks, so with the merged files one page that used a form helper put `@voltro/client`'s form engine, `@tanstack/form-core` and its schema code into the entry chunk every page loads (+26.3 KB gzip on an app with one form page). Your app's bundle now splits these packages as finely as their source. Import paths and public exports are unchanged.
|
|
257
|
+
- **Reduce runtime loading for primary SQL commands and storage configuration** — `@voltro/cli`, `@voltro/plugin-storage`
|
|
258
|
+
|
|
259
|
+
Load memory-store, replica-routing and URL-ingestion runtime code only when used, retaining the shared store behavior and all URL safety checks.
|
|
260
|
+
- **Projected HTTP failures reuse private schema tag metadata** — `@voltro/protocol`
|
|
261
|
+
|
|
262
|
+
Mounted public API routes reuse private metadata for immutable Effect error schema ASTs instead of retraversing tagged schemas on each failure. Replacement schemas, per-failure status and headers, shared guard errors and undeclared failure behavior remain visible.
|
|
263
|
+
- **Distinguish requested tenant refusal from rejected credentials** — `@voltro/protocol`, `@voltro/runtime`
|
|
264
|
+
|
|
265
|
+
Request-local tenant selection now reports `RequestedTenantRefused` separately from rejected credentials. Its typed reason describes an invalid target, missing membership or unavailable selection source. HTTP and WebSocket middleware preserve the error, and live computed subscriptions terminate when workspace access is withdrawn. Credential rejection still reports `Unauthenticated`; clients must reserve session invalidation for that error. No user-authored API is removed or renamed.
|
|
266
|
+
- **Namespaced web optimization skill for customer projects** — `@voltro/cli`
|
|
267
|
+
|
|
268
|
+
Seed `voltro-optimize-web` for website and UI optimization, with one canonical Claude skill and a Codex reference to the same instructions. All supplied Voltro skills use the `voltro-` namespace. Existing project-owned skills are preserved.
|
|
269
|
+
|
|
270
|
+
### Fixed
|
|
271
|
+
|
|
272
|
+
- **Wait for RPC readiness before sending actions** — `@voltro/client`
|
|
273
|
+
|
|
274
|
+
Wait for RPC protocol recovery before sending new actions after a detected connection outage, including reads retried during API reconnection. Resolve the current runtime after readiness, bound the wait and cancel queued intent on user or organization changes. Preserve SSR/StrictMode boot resolution. Already sent actions are not automatically replayed after an uncertain response.
|
|
275
|
+
- **Preserve audit and row-history actor snapshots on real stores** — `@voltro/database`
|
|
276
|
+
|
|
277
|
+
Resolve audit and row-history actor snapshots with canonical database predicates, so real stores preserve the matching actor identity instead of silently dropping it.
|
|
278
|
+
- **Schedules and workflows can refresh live aggregates** — `@voltro/cli`, `@voltro/runtime`
|
|
279
|
+
|
|
280
|
+
Scheduled and workflow handlers receive the live aggregate registry in development and production. Calendar cron policies already execute at the declared timezone boundary; stale type comments no longer describe them as deferred.
|
|
281
|
+
- **Validate artifact batches before publishing bytes** — `@voltro/cli`
|
|
282
|
+
|
|
283
|
+
Release file uploads reject missing or conflicting metadata flags before uploading. Desktop publishing validates manifest metadata, complete file batches and declared signatures against their hashes and sizes before remote writes, then uploads private stable snapshots without retaining the whole batch in memory. Release upload keeps its artifact `--url` separate from API selection through `--target`.
|
|
284
|
+
- **Audit appends recover without artificial chain gaps** — `@voltro/plugin-audit`
|
|
285
|
+
|
|
286
|
+
Datastore audit sinks serialize each writer's append attempts and derive links from persisted state where reads are available. Failed inserts no longer consume chain positions. A stable optional receipt ID lets durable delivery handlers deduplicate identical retained events across retries and restarts; different content or tampered receipts are refused. Default best-effort audit capture remains unchanged.
|
|
287
|
+
- **Hydrate automatic islands as interruptible React work** — `@voltro/web`
|
|
288
|
+
|
|
289
|
+
Schedule automatic island hydration as interruptible React work while retaining foreground hydration for interaction-triggered islands.
|
|
290
|
+
- **Stream billing read models after subscription changes** — `@voltro/plugin-billing`
|
|
291
|
+
|
|
292
|
+
Billing subscription and entitlement queries now stream updates when their subscription rows change. Invoice queries react to invoice and customer-link changes. Existing subscribers receive the current result after cancellation, resumption, provider webhooks or reconciliation without polling or reloading. Tenant and permission checks remain active on every recomputation.
|
|
293
|
+
- **Billing private URLs recheck current authority after provider IO** — `@voltro/plugin-billing`
|
|
294
|
+
|
|
295
|
+
Checkout and customer-portal URL routes reauthenticate their bound principal and tenant and recheck the declared manager guard before returning a provider URL. Withdrawal during provider IO refuses the late result without undoing or replaying the provider operation.
|
|
296
|
+
- **Prioritize rendering assets during browser startup** — `@voltro/cli`
|
|
297
|
+
|
|
298
|
+
Production browser entries use native ESM dependency loading instead of generated JavaScript module-preload hints. This avoids synchronous link insertion during route loading. Deferred features still load their CSS before module execution.
|
|
299
|
+
- **Preserve all own JSON fields in approval identities** — `@voltro/runtime`
|
|
300
|
+
|
|
301
|
+
Preserve all own JSON fields when deriving approval payload identities, including `__proto__`. Nested content changes can no longer disappear through an object prototype setter during canonicalization. Object key ordering stays stable; inherited fields remain excluded.
|
|
302
|
+
- **The MySQL/MariaDB CDC gate now cancels paired expiry timers, as 0.85.0 described** — `@voltro/sql-mysql`
|
|
303
|
+
|
|
304
|
+
The 0.85.0 notes listed "Release paired CDC expiry timers immediately", but that code did not reach the release: the inline/binlog delivery gate still counted unpaired occurrences and let every timer run out. It now cancels a fingerprint's timer as soon as its echo pairs, and each expiry belongs to its own generation, so an old callback cannot expire a newer identical write. Unpaired events keep their expiry and first-arrival delivery.
|
|
305
|
+
- **Restore Cloud environment CLI access and faithful non-secret export** — `@voltro/cli`
|
|
306
|
+
|
|
307
|
+
Cloud environment commands accept `--env <slug>` and report the actual selected environment. Without it, the configured project default applies. Listings and dotenv export contain only non-secret project variables; CLI writes cannot turn an existing secret into plaintext. Current role, membership and credential authority remain required.
|
|
308
|
+
|
|
309
|
+
`env pull` atomically writes an owner-only `.env.cloud` and validates faithful dotenv literals before changing the previous file. Invalid targets, responses or unrepresentable values refuse without overwriting it. Use a dotenv reader rather than sourcing the file as a shell script. No user-authored source migration is required.
|
|
310
|
+
- **Bind Cloud rollback to its inspected environment and artifact** — `@voltro/cli`
|
|
311
|
+
|
|
312
|
+
The launch-gated Cloud rollback command now inspects and submits the exact environment, current and previous deployment IDs, image reference, digest and commit. Explicit `--env` selection cannot silently fall back; omitted selection uses the configured app override or project default. A changed target is refused, and repeated confirmations reuse the matching in-flight queue item.
|
|
313
|
+
|
|
314
|
+
`--no-watch` returns after queue acknowledgement; together with `--json` it prints that acknowledgement as JSON. The normal command still waits for a terminal deployment status. An accepted queue item does not establish runtime health. The launch default remains closed. No user-authored source migration is required.
|
|
315
|
+
- **CMS template content reads serialize native timestamps** — `@voltro/cli`
|
|
316
|
+
|
|
317
|
+
The CMS API template serializes native database timestamps as ISO strings in draft, published and release-perspective content reads. Existing documents now open through the JSON output boundary without rejecting their stored creation timestamps.
|
|
318
|
+
- **Preserve request-scoped preload seeds in compiled server renders** — `@voltro/client`, `@voltro/web`, `@voltro/cli`
|
|
319
|
+
|
|
320
|
+
Compiled server renders preserve subscription and client-store seeds when page and renderer code load separate framework module instances. Resolver hooks and the server's AsyncLocalStorage share one process identity, while each request retains its own seed bags. Returned SSR and deferred SPA stream callbacks restore the captured request scope when the HTTP handler dispatches them. Concurrent renders cannot mix hydration data.
|
|
321
|
+
|
|
322
|
+
Existing seeding APIs and their server-only contract remain unchanged. Live subscriptions still replace initial seeds after the browser connects.
|
|
323
|
+
- **Migration recovery validates the complete execution receipt** — `@voltro/database`, `@voltro/cli`
|
|
324
|
+
|
|
325
|
+
Resuming a migration whose completed history already exists now validates the exact ordered operation receipt, including serialized defaults and duplicate counts. A reordered, incomplete or extra-operation receipt refuses recovery without clearing the operation ledger or replacing historical metadata. Valid recovery retains the original receipt and skips work already completed.
|
|
326
|
+
- **Computed queries stop delivering after authentication ends** — `@voltro/runtime`
|
|
327
|
+
|
|
328
|
+
Computed query subscriptions report `Unauthenticated` and stop delivering when authentication is withdrawn during recomputation, as they already do for `ScopeError`. They no longer silently remain on the last value after a session ends or an account is deactivated. Restoring access requires a newly authorized subscription; ordinary non-authorization recompute failures retain their existing behavior.
|
|
329
|
+
- **Computed queries observe both sides of tenant transitions** — `@voltro/runtime`
|
|
330
|
+
|
|
331
|
+
Computed subscriptions now invalidate when a source row moves out of their tenant as well as into it. This rechecks ongoing authorization before recomputation and terminates revoked subscriptions with the typed refusal. Events confined to other tenants retain the existing fast skip.
|
|
332
|
+
- **Computed queries terminate on typed access revocation** — `@voltro/runtime`
|
|
333
|
+
|
|
334
|
+
Reactive computed queries preserve typed `ScopeError` and `Unauthenticated` failures from Effect executors during recomputation. The dispatcher ends the revoked subscription with its access error instead of treating Effect's failure wrapper as a temporary computation failure and silently retaining the last value. Request cancellation still interrupts the computation. Ordinary transient computation failures retain the existing recovery behavior.
|
|
335
|
+
|
|
336
|
+
Custom access checks must declare their authority tables in the query's `source` alongside its data dependencies. The regression tests run the actual producer and dispatcher through source changes, access revocation and subsequent regrant.
|
|
337
|
+
- **Recheck computed query authority across source workspace IDs** — `@voltro/protocol`, `@voltro/runtime`
|
|
338
|
+
|
|
339
|
+
Computed subscriptions now recheck every change to a declared source even when its row carries another workspace's tenant ID. Global account and membership state can affect subscriptions across workspaces; row-image routing could leave those subscriptions with stale authority. Executors still enforce current access, live reauthorization preserves requested-tenant refusals as typed denials, revoked subscriptions terminate, and unchanged results are suppressed. Descriptor row matching is unchanged. No user-authored API is removed or renamed.
|
|
340
|
+
- **Recognize configured test setup files as fixture consumers** — `@voltro/cli`
|
|
341
|
+
|
|
342
|
+
Doctor parses configured Vitest setupFiles and globalSetup entries, including static aliases, spreads, roots and nested projects, without executing configuration. Test-only helper chains may import fixtures; production paths still fail the boundary.
|
|
343
|
+
- **Confirmation controls never submit surrounding editor forms** — `@voltro/devtools-ui`
|
|
344
|
+
|
|
345
|
+
`ConfirmMutationButton` and `ConfirmAction` use non-submit buttons for opening, confirming and cancelling an operation. Placing a confirmation inside an editor no longer also submits that enclosing form. Only the explicit confirmation callback runs the operation.
|
|
346
|
+
- **Confirmation dialogs keep viewport gutters and bounded height** — `@voltro/devtools-ui`
|
|
347
|
+
|
|
348
|
+
Confirmation dialogs use valid calculated width and height limits, preserving viewport gutters on mobile and scrolling long content within the available height. Their shared confirmation panel uses the standard one-pixel accent edge.
|
|
349
|
+
|
|
350
|
+
Dashboard hosts scan nested built shared UI modules so confirmation positioning and other component utilities remain in generated CSS after modular builds.
|
|
351
|
+
- **Mutation confirmations survive disappearing live rows** — `@voltro/devtools-ui`
|
|
352
|
+
|
|
353
|
+
`ConfirmMutationDialog` lets a stable parent retain an operation's captured selection when optimistic or live changes remove its trigger. The dialog keeps pending requests locked and refusals visible instead of losing them with a row. Confirmations initially focus Cancel and can use `returnFocusTo` to restore keyboard focus when the original trigger disappears. `ConfirmMutationButton` uses the same implementation.
|
|
354
|
+
- **Data explorer waits for verified table metadata** — `@voltro/devtools-ui`
|
|
355
|
+
|
|
356
|
+
The data explorer now shows clear loading, retryable failure and unavailable-table states before rendering a selected table without its catalogue metadata. It no longer displays a zero-column grid, row actions or misleading pagination after an initial catalogue failure. Failed refreshes retaining verified metadata keep the grid, reviewed drafts and pending write locks in place.
|
|
357
|
+
- **Descriptor queries decode encrypted storage** — `@voltro/cli`
|
|
358
|
+
|
|
359
|
+
Development and compiled production API starts now give the reactive dispatcher the existing storage codec. A descriptor query's initial RPC snapshot and live deltas return decoded `.encrypted()` values instead of physical ciphertext. Request-scoped writes remain encrypted at rest. Query authorization, row filters, wire-column visibility and production inspection masking remain independent.
|
|
360
|
+
|
|
361
|
+
The regression exercises an actual production RPC listener, the normal REST projection, a dispatcher snapshot and a persisted update followed by its live delta. Separate native acceptance checks cover a compiled PostgreSQL target.
|
|
362
|
+
- **Preserve universal desktop targets and offer compatible update artifacts** — `@voltro/cli`, `@voltro/plugin-releases`
|
|
363
|
+
|
|
364
|
+
Desktop manifests retain universal macOS targets and use explicit Rust build context for unqualified bundle paths instead of assuming Intel. Ambiguous targets are refused before uploads. Signed universal Mac updates serve both native architectures while signed concrete builds take precedence; architecture-specific downloads refuse incompatible fallbacks.
|
|
365
|
+
- **Development-only diagnostics work again in apps installed from npm** — `@voltro/web`, `@voltro/devtools`
|
|
366
|
+
|
|
367
|
+
The published packages had `import.meta.env.DEV` replaced with `false` when they were built, so under `voltro dev` an app installed from npm never forwarded browser `console.*` calls to the terminal, never relayed server logs into the browser console, never showed the trace error overlay, never armed the reload backstop for a wedged socket and never printed the islands-mode notice. The packages now leave `import.meta.env` to your app's build, which keeps all of these in development and removes them from production.
|
|
368
|
+
- **Development islands retain configured font assets** — `@voltro/cli`
|
|
369
|
+
|
|
370
|
+
Development islands pages retain configured font definitions, CSS variables and preloads in their initial HTML, matching built pages without loading the full application entry. This prevents pages from falling back to the browser's default typeface.
|
|
371
|
+
- **Devtools capsule keeps form labels readable** — `@voltro/devtools`
|
|
372
|
+
|
|
373
|
+
The closed Devtools capsule now considers visible form labels and fieldset legends when temporarily moving away from page content. This keeps field names and accompanying status text readable on narrow screens while preserving the saved dock position and keyboard access to the overlay.
|
|
374
|
+
- **Developer capsule keeps interactive content clear** — `@voltro/devtools`, `@voltro/devtools-ui`
|
|
375
|
+
|
|
376
|
+
Keep the closed development capsule clear of table cells and live/error messages, including updates to existing text nodes. Use a compact accessible status mark when only a narrow free area remains, restoring the full face when space becomes available. Preserve the saved dock position. Data explorer query errors announce themselves as alerts while retaining their existing recovery controls.
|
|
377
|
+
- **Keep the closed Devtools capsule clear of page controls** — `@voltro/devtools`
|
|
378
|
+
|
|
379
|
+
The closed in-page Devtools capsule temporarily moves to nearby free space when it would cover a visible page control, heading or paragraph. Layout changes are observed without polling or network calls. The saved dock preference is preserved, and dragging starts from the displayed position. A viewport without free space retains the preferred position.
|
|
380
|
+
- **Companion warnings follow the resolved dependency graph** — `@voltro/cli`
|
|
381
|
+
|
|
382
|
+
The Effect companion warning follows the app and CLI dependency links. Unreferenced pnpm virtual-store folders left behind by an override and frozen install no longer keep a mismatch warning active; reachable transitive mismatches still produce the repair instruction.
|
|
383
|
+
- **Effect 4 migration repairs asserted Result value reads** — `@voltro/cli`
|
|
384
|
+
|
|
385
|
+
`voltro update` repairs `left`/`right` reads through TypeScript assertions, non-null assertions, parentheses, local aliases and literal bracket keys when the value comes from a Result decoder or guard. Inline assertion fields become `failure`/`success` as well; computed keys and named assertion types receive review markers. A repair codemod also reaches projects already upgraded to Effect 4.
|
|
386
|
+
|
|
387
|
+
Saved schema decoders and unevaluated Effects or Promises retain their own metadata. The repair follows decoder calls and Results extracted through `yield*`, `Effect.runSync` or awaited `Effect.runPromise`. Reassigned bindings and conflicting declarations receive manual-review markers instead of guessed field renames, and alias discovery terminates on conflicting origins.
|
|
388
|
+
- **Effect 4 upgrades flag ignored schema parsing annotations** — `@voltro/cli`
|
|
389
|
+
|
|
390
|
+
`voltro update` warns when a schema annotation supplies `parseOptions`, including through local annotation aliases and spreads. Effect 4 ignores these annotations: move parsing options to decoder calls, or enforce reusable strict contracts with `StructWithRest` and `isPropertyNames` checks. Test unknown-key rejection at the actual decoding boundary; the repair codemod also reaches already-upgraded projects.
|
|
391
|
+
- **Redact credentials embedded in diagnostic strings** — `@voltro/logger`
|
|
392
|
+
|
|
393
|
+
The default logger recognizes its supported credential formats inside longer strings and replaces the entire message, field or plain-object cause value. Ordinary identifiers and non-sensitive explanations remain unchanged. Explicit key-only redactors retain their existing behavior.
|
|
394
|
+
|
|
395
|
+
Cloud organization-operator capture sanitizes retained target and explanation text with the same primitive. Privileged history reads also sanitize older context without rewriting its stored bytes or audit hash chain. Format matching does not classify arbitrary opaque secrets or `Error` instance internals.
|
|
396
|
+
- **A precompiled voltro start validates env fields with their own effect copy** — `@voltro/env`, `@voltro/cli`
|
|
397
|
+
|
|
398
|
+
A web app whose `app.config.ts` declares an `envVar.url()` refused to boot under a precompiled `voltro start` as soon as that variable was set: `FATAL: precompiled voltro start failed to load: Cannot convert a Symbol value to a string`. The start bundle validated the app's env schemas with its own copy of `effect`, whose parser does not recognise the other copy's internal sentinels, so the URL check received a symbol. Every env field now carries a decoder bound to the copy that built its schema, and boot validation goes through it.
|
|
399
|
+
|
|
400
|
+
`EnvFieldSpec` and `AnyEnvFieldSpec` gain a required `decode`. Fields are produced by the `envVar.*` builders, which set it; a field object written by hand would now need it, which no documented API asks for.
|
|
401
|
+
- **errorMessage in an error boundary no longer pulls Effect into every page** — `@voltro/protocol`, `@voltro/client`
|
|
402
|
+
|
|
403
|
+
`errorTag`, `errorMessage` and `reportableError` lived in the module that defines the procedure schemas, so calling `errorMessage` in an `error.tsx` — which is in every page's entry — put Effect's schema code into the first load of every page (about 140 KB raw). They now live in a module of their own with no imports. Imports and behavior are unchanged.
|
|
404
|
+
- **Redact recognized credential formats from diagnostic values** — `@voltro/logger`
|
|
405
|
+
|
|
406
|
+
Recognize source-defined Voltro service, invitation and publish credential value formats in the default logger redactor, including bare values and embedded URLs or diagnostic text. Match the full token body and identifier boundaries so ordinary invitation IDs and longer unrelated identifiers retain their meaning. Explicit key-only redactors keep their existing contract.
|
|
407
|
+
|
|
408
|
+
Cloud audit capture and customer/operator history projection reuse this shared format policy. Historical bytes and hash chains remain unchanged; projection does not erase secrets stored by an older policy.
|
|
409
|
+
- **Doctor recognizes configured Vitest setup consumers** — `@voltro/cli`
|
|
410
|
+
|
|
411
|
+
The fixture import audit recognizes statically configured Vitest setupFiles and globalSetup entries, including test helpers they import. Only the selected configuration’s statically reachable default export grants an exemption; unused examples, overridden declarations and unknown configuration remain checked. Configurations are read without execution. Side-effect imports and production paths into a setup entry remain checked.
|
|
412
|
+
- **Invalid fluent filters fail before reads or writes** — `@voltro/runtime`
|
|
413
|
+
|
|
414
|
+
Fluent `where` filters now reject unsupported comparison operators, invalid argument counts, and non-string column names before accessing data. An unknown operator no longer silently becomes equality for selects, updates, or deletes. Use `contains` or `startsWith` for literal text matching; two-argument equality and predicate helpers keep their existing behavior.
|
|
415
|
+
- **Gateway catalogs retain new model types under the unknown filter** — `@voltro/ai`
|
|
416
|
+
|
|
417
|
+
Both gateway catalog readers normalize unrecognized model types to `unknown`, keeping them visible through the existing modality filter when a supported AI SDK update introduces new classifications. The HTTP catalog preserves the original model type in `raw`.
|
|
418
|
+
- **voltro dev no longer corrupts the app's .gitignore while booting** — `@voltro/cli`
|
|
419
|
+
|
|
420
|
+
`voltro dev` adds `app.manifest.generated.json`, `app.graph.observed.generated.json` and, when it mints a secret, `.env.local` to the app's `.gitignore` from separate boot tasks. Their read-modify-write cycles interleaved: projects ended up with lost entries and stray fragments such as `a` or `.json`, and generated files the framework meant to ignore were committed. One writer now adds missing lines in a single synchronous step, never rewrites what is there, and reports instead of failing when the file cannot be written.
|
|
421
|
+
|
|
422
|
+
Check your apps' `.gitignore` files for single-word fragments left by earlier boots, and untrack `app.manifest.generated.json` / `app.graph.observed.generated.json` if they were committed.
|
|
423
|
+
- **Preserve row refusal details in import status records** — `@voltro/data-transfer`
|
|
424
|
+
|
|
425
|
+
Keep the bounded row refusal report in error messages and the first terminal import-status update. Polling clients receive concrete row reasons and counts instead of only the error tag, while typed error handling and wire schemas remain unchanged.
|
|
426
|
+
- **Inspect access discards sensitive drafts on permission withdrawal** — `@voltro/devtools-ui`
|
|
427
|
+
|
|
428
|
+
The shared inspect-access form discards unsaved credentials and endpoint/removal drafts when configure permission is withdrawn or a typed access/session refusal arrives. Restored access starts with a fresh form; late saves from the old form cannot clear a new draft or claim success. Transient read failures retain unfinished drafts. Typed write refusals use readable localized feedback.
|
|
429
|
+
- **Served app inspection endpoint setup** — `@voltro/devtools-ui`
|
|
430
|
+
|
|
431
|
+
Inspection access forms can expose a served-app base URL when the host supports endpoint editing. Hosts that own endpoint discovery retain their credential-only form. Invalid saves preserve drafts; fresh observations update untouched URL fields. Read and write credentials remain separate and saving confirms configuration, not connectivity.
|
|
432
|
+
- **Inspect credential validation guides corrections at the affected field** — `@voltro/devtools-ui`
|
|
433
|
+
|
|
434
|
+
Inspect credential forms focus and mark the affected field when a host supplies a field-specific validation error. Unknown save failures show write guidance, while rejected drafts remain available for correction and access revocation still clears sensitive inputs.
|
|
435
|
+
- **Inspect credential inputs wait until the browser form is ready** — `@voltro/devtools-ui`
|
|
436
|
+
|
|
437
|
+
Server-rendered inspect credential forms keep endpoint, token, and removal controls disabled until browser handlers are ready. Early input can no longer disappear during hydration. The form exposes its preparing state to assistive technology; deliberate saves remain available once ready.
|
|
438
|
+
- **Data explorer drafts survive acknowledgements of the current query** — `@voltro/devtools-ui`
|
|
439
|
+
|
|
440
|
+
The data explorer retains an active record draft when the router acknowledges the same query. Changing the table, filter, search, sort or page still opens a fresh view. Reconnection and later external updates do not automatically replay writes.
|
|
441
|
+
- **Show readable inspection permission and session failures** — `@voltro/devtools-ui`
|
|
442
|
+
|
|
443
|
+
Show localized permission and session explanations in shared inspection sections and the migrations panel instead of rendering structured transport objects as `[object Object]`. Keep readable local error messages and provide clear recovery guidance without dumping unknown transport payloads.
|
|
444
|
+
- **Inspection views explain protected values** — `@voltro/devtools-ui`
|
|
445
|
+
|
|
446
|
+
Inspection tables and record details explain sensitive, server-only and encrypted mask markers instead of displaying transport JSON. Protected values have no editor or reference action and are omitted when copying a record. Ordinary JSON remains unchanged; column metadata preserves the three independent protection axes.
|
|
447
|
+
- **Inspection reconnects retain the last observed values** — `@voltro/devtools-ui`
|
|
448
|
+
|
|
449
|
+
The data explorer explains that disconnected live updates retain the last observed values until recovery. It no longer promises successful periodic snapshots while the connection is unavailable.
|
|
450
|
+
- **Stable identifiers across island hydration roots** — `@voltro/web`, `@voltro/cli`
|
|
451
|
+
|
|
452
|
+
Islands preserve React identifiers for labels, inputs and SVG references across server rendering and client hydration, including repeated instances and framework runtime wrappers. Nested islands share their hydrating parent's root. The independent-root context and hydration prefix add about 0.5 KB gzipped to the reference islands entry, which remains below 70 KB; its measured chunk budgets are updated accordingly.
|
|
453
|
+
- **Island previews preserve state in other development pages** — `@voltro/cli`
|
|
454
|
+
|
|
455
|
+
Opening or refreshing an islands preview no longer reloads unrelated development pages or discards their unsaved state. Identical generated entries retain their files; changed island entries update only the clients using them, while source edits continue to use normal hot updates.
|
|
456
|
+
- **ISR nonce refusals explain the same remedies in dev and production** — `@voltro/cli`
|
|
457
|
+
|
|
458
|
+
`voltro dev` and `voltro start` now give the same route-specific diagnostic when middleware supplies `cspNonce` for an ISR page. The shared refusal runs before serving cached ISR HTML; use `renderMode = 'ssr'` for per-request nonces or a hash-based CSP for ISR.
|
|
459
|
+
- **Keep RPC startup code off API-free production pages** — `@voltro/client`, `@voltro/web`
|
|
460
|
+
|
|
461
|
+
Production apps without configured APIs use shared browser bootstrap primitives without eagerly loading RPC transports or the form engine. API apps, development tools and enabled debug sessions load the full runtime before mounting. Loader queries load their existing reader on demand. Shared contexts, seeded stores, lookup errors and SSR hydration tree IDs remain consistent.
|
|
462
|
+
- **Start query reactions after workflow readiness** — `@voltro/cli`
|
|
463
|
+
|
|
464
|
+
Attach query-watching reactions after the workflow runtime is ready during development startup. The first delivery can start its workflow immediately without waiting for a later resync after an initialization failure.
|
|
465
|
+
- **Existing subscriptions refresh authentication and permissions before delivery** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/plugin-rbac`
|
|
466
|
+
|
|
467
|
+
Live queries, event subscriptions and streams now rerun their original authentication chain and refresh role-derived permissions before delivering data. Revoked credentials, memberships or grants end existing subscriptions with a typed refusal. Computed queries, row filters and column access use refreshed authority; the original identity and tenant remain bound to the subscription.
|
|
468
|
+
|
|
469
|
+
Configured authentication and scope caches retain their documented invalidation windows. Independent delivery snapshots prevent concurrent refreshes from mixing subject metadata and permissions. No migration is required.
|
|
470
|
+
- **Live computed reads report failures and recover automatically** — `@voltro/runtime`
|
|
471
|
+
|
|
472
|
+
An already subscribed computed query now reports ordinary recomputation failures through the existing in-band error channel as a sanitized `QueryRecomputeFailed`. The client retains its last successful data alongside that error, so applications can distinguish stale data from a current result without exposing server details.
|
|
473
|
+
|
|
474
|
+
The runtime retries failed reads with exponential delays from one to thirty seconds. A successful retry sends a snapshot even when the value is unchanged, clearing the error without another source mutation or a page reload. Source changes can recheck sooner. Reads for one subscription are serialized and intervening changes are coalesced before delivery.
|
|
475
|
+
|
|
476
|
+
Unsubscribe and server shutdown cancel pending retries and suppress late results. Every retry uses the existing executor and its current access checks; typed access withdrawal remains terminal. Mutations and actions are not replayed.
|
|
477
|
+
- **Live queries withdraw access when credentials expire** — `@voltro/runtime`
|
|
478
|
+
|
|
479
|
+
Live row and computed queries now report `Unauthenticated` when their credential expires, so applications can withdraw protected views instead of retaining a silently completed subscription. The refusal remains isolated to the affected subscription.
|
|
480
|
+
- **Workflow permission checks refresh current caller authority** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`
|
|
481
|
+
|
|
482
|
+
`ctx.access.require(scope)` now refreshes bound authentication and permissions when its Effect executes, including repeated execution of the same guard. Durable workflows retain an identity-bound resolver, so later permission checks observe credential expiry, revocation and current grants. Authority-source failures abort the operation. These checks do not interrupt an already executing external operation or replace transactional authorization fences.
|
|
483
|
+
- **Reload middleware when its imported sources change** — `@voltro/cli`
|
|
484
|
+
|
|
485
|
+
Web development restarts when any transitive middleware value-import changes, including shared sources outside the web app and helpers inside src. Type-only imports do not trigger replacement.
|
|
486
|
+
- **Render service outages with an explicit HTTP status** — `@voltro/web`, `@voltro/cli`
|
|
487
|
+
|
|
488
|
+
Middleware can return responseStatus alongside responseHeaders, for example 503 with Retry-After. Dev and built start preserve the status on streamed SSR, buffered SSR and SPA shells. Non-200 ISR responses bypass the cache. Loader redirects and notFound retain their own status.
|
|
489
|
+
- **Named plugin instances pass RPC code generation** — `@voltro/cli`
|
|
490
|
+
|
|
491
|
+
RPC code generation accepts the `#` separator used by named plugin installations while preserving their wire names. Canonical and named instances generate distinct valid JavaScript identifiers. Malformed names and names that collide after identifier normalization are still refused before a client group is written.
|
|
492
|
+
- **Include transitive JavaScript imports in build reports** — `@voltro/cli`
|
|
493
|
+
|
|
494
|
+
Include transitive static JavaScript imports in build reports without relying on preload hints, retaining CDN asset prefixes and identifying older artifacts without a recorded graph.
|
|
495
|
+
- **Outgoing webhook storage is declared at boot** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
496
|
+
|
|
497
|
+
Apps that declare `@voltro/plugin-webhooks` in their own package manifest now include the webhook framework tables in their schema even when they only have outgoing `*.event.ts` declarations. Previously the feature detector only recognized incoming `*.webhook.tsx` files, leaving outgoing delivery storage unavailable.
|
|
498
|
+
|
|
499
|
+
Dev, build/serve, database planning/apply and migrations use the same manifest declaration. Workspace package availability alone does not enable storage in an unrelated event app. Signing secrets remain encrypted and require the configured field-encryption key in production.
|
|
500
|
+
|
|
501
|
+
All four canonical webhook tables are now included in the shared framework schema: targets, deliveries, rate windows, and event statistics. This also removes the stale event-statistics assembly exception; plugin discovery and CLI schema generation agree.
|
|
502
|
+
|
|
503
|
+
Dashboard target creation can now carry an explicit application tenant ID through shared UI, Cloud and native Inspect. The form requires the selection and keeps it on failures; native tenant refusals return a tenant field error. The transport field is additive and optional for existing callers; tenant-table constraints are unchanged.
|
|
504
|
+
|
|
505
|
+
Webhook snapshots normalize both native Date objects and serialized SQL timestamps to ISO strings. The regression uses real stored rows in both representations and still excludes signing material and payloads.
|
|
506
|
+
|
|
507
|
+
Webhook Inspect activity uses the canonical typed equality predicate, so delivery history is scoped to its event in native SQL stores as well as memory. Snapshot reads now provide complete query descriptors instead of bypassing descriptor type checking.
|
|
508
|
+
- **Plain storage keys isolate configured instances** — `@voltro/plugin-storage`
|
|
509
|
+
|
|
510
|
+
Separate new plain object keys by configured storage instance and logical bucket while preserving tenant prefixes and stored reference keys. Keep other instances’ existing shared-key references in physical deletion counts, preventing deletion of their still-referenced bytes on a shared backend. No user-authored API change is required; low-level service construction can supply the canonical storageInstance explicitly. Durable shared-generation and independent-provider existing-key reconciliation remain incomplete.
|
|
511
|
+
- **Preloaded subscriptions follow confirmed initial authority** — `@voltro/client`
|
|
512
|
+
|
|
513
|
+
Prevent preloaded subscription hooks from reapplying old SSR values or failure markers after an identity-changing reconnect or whole-cache resnapshot. New reads and remounted hooks wait for their own snapshot; ordinary socket recovery and same-subject token refresh retain the existing cache behavior. Apps must still gate workspace changes on a matching server context.
|
|
514
|
+
- **Preserve destination storage catalogue identity during logical transfers** — `@voltro/data-transfer`
|
|
515
|
+
|
|
516
|
+
Logical data exports omit the deployment's storage reference catalogue identity, and imports preserve the destination's identity even when an older or explicit-table bundle contains a foreign marker. Native disaster-recovery backup and restore continue to preserve the saved marker.
|
|
517
|
+
- **Query results recheck current authority at completion** — `@voltro/runtime`
|
|
518
|
+
|
|
519
|
+
Computed first snapshots and recomputations, descriptor subscription snapshots and deltas, and one-shot REST reads recheck current authentication and declared guards after awaited work. Revocation during a handler or store read discards the completed result and returns a typed refusal. Existing read-start checks remain in place; configured authentication and scope-cache windows still apply.
|
|
520
|
+
|
|
521
|
+
Existing guards are reused. No public API or user-authored source change is required. Guard callbacks remain read-only and may run at both read boundaries.
|
|
522
|
+
- **Invalidate returned database queries through declared sources** — `@voltro/runtime`, `@voltro/database`
|
|
523
|
+
|
|
524
|
+
Reactive queries that return database builders now retain their declared `source` dependencies alongside SQL dependencies. Authority-table changes recheck subscription guards and reread the finalized descriptor without waiting for a result-table write. Explicit sources invalidate independently of projected columns and row predicates; unchanged authorized results still suppress redundant deltas.
|
|
525
|
+
|
|
526
|
+
`QueryDescriptor.dependentTables` carries additional invalidation metadata without adding SQL joins, predicates or tenant access. Computed queries continue to rerun their executors; returned database descriptors continue to produce row snapshots and deltas.
|
|
527
|
+
- **Keep authentication prefix conflict warnings readable** — `@voltro/cli`
|
|
528
|
+
|
|
529
|
+
Authentication strategy prefix conflict warnings retain the conflicting prefix, strategy names and corrective guidance under the default credential redaction policy. Credential values remain redacted.
|
|
530
|
+
- **Redis read-your-writes receives broadcasts during subscription acknowledgement** — `@voltro/runtime`
|
|
531
|
+
|
|
532
|
+
The Redis RYW mirror installs its message listener before issuing SUBSCRIBE. This retains a peer's first broadcast if Redis delivers it before the local subscription promise settles. Channel and sender filtering remain unchanged.
|
|
533
|
+
- **Reference cleanup boots alongside application row filters** — `@voltro/cli`
|
|
534
|
+
|
|
535
|
+
`voltro dev` and `voltro serve` initialize reference cleanup with an explicit internal row scope, so registering application row filters no longer aborts boot. Cleanup can repair hidden orphaned rows while retaining its reference and tenant constraints; application callers keep their normal row filters.
|
|
536
|
+
- **Validate release artifact metadata before persistence** — `@voltro/plugin-releases`
|
|
537
|
+
|
|
538
|
+
Validate required release artifact metadata before persistence and treat stored blank signatures as unsigned in release lists, promotion checks, and Tauri feeds.
|
|
539
|
+
- **Fence artifact attachment to the current release target** — `@voltro/plugin-releases`
|
|
540
|
+
|
|
541
|
+
Add an optional appPrefix constraint to release artifact uploads for server adapters that delegate an authenticated app namespace. Claim exact target identity with its revision, reread it under the acquired fence, and lock the current release association before committing artifact and audit metadata. A changed association or target identity refuses without partial metadata changes.
|
|
542
|
+
- **Validate release file destinations before Storage writes** — `@voltro/plugin-releases`, `@voltro/cli`
|
|
543
|
+
|
|
544
|
+
# Release upload destination admission
|
|
545
|
+
|
|
546
|
+
Release file uploads now validate the destination before requesting Storage tickets or transferring bytes. The shared preparation query checks release ownership, app, version and explicit target revision; final metadata remains constrained to the resolved app and version. Cloud release-bound tickets use the same project-scoped preparation, while standalone project Storage uploads remain supported.
|
|
547
|
+
|
|
548
|
+
Deploy the updated API before using the updated file-upload CLI. Custom publish facades must expose the `prepare-upload` management action and preserve its destination constraints. This change does not provide transfer reservation, durable retries or orphan cleanup.
|
|
549
|
+
- **Request tests expose byte-stream response bodies** — `@voltro/testing`
|
|
550
|
+
|
|
551
|
+
`makeTestApp` consumes finite byte streams and exposes their exact `bytes` and decoded `text`, retaining the streaming marker. SSE responses remain unconsumed.
|
|
552
|
+
- **Handler defects preserve sibling live queries** — `@voltro/runtime`
|
|
553
|
+
|
|
554
|
+
Unexpected RPC handler failures now return a sanitized, request-correlated failure on WebSocket and HTTP transports. Other live queries on the connection continue receiving updates, so a deliberate retry after a failed write does not leave existing views frozen. Defect logging includes the request identifier; protocol defects remain connection-wide, and failed writes are not replayed automatically.
|
|
555
|
+
- **Use the framework trace generator for REST rate limits** — `@voltro/plugin-ratelimit`
|
|
556
|
+
|
|
557
|
+
Hand-authored REST rate-limit checks use the shared framework trace-ID generator, including checks that refuse the route before its handler runs. The runtime dependency is included in production installations.
|
|
558
|
+
- **Reviewed migration plans refuse changes after lock waits** — `@voltro/cli`, `@voltro/database`
|
|
559
|
+
|
|
560
|
+
`voltro db apply --plan` checks the complete reviewed schema and ordered operations again under the migration lock, before executing the plan. Concurrent schema changes now refuse with exit 2 instead of silently introducing unreviewed operations. Programmatic `applyPlan` callers receive an execution or convergence phase in their replan callback, so review checks do not reject the expected post-write empty plan. Ordinary automatic migrations continue to replan against the current schema.
|
|
561
|
+
- **Reviewed migration plans retain their exact operations and risk summary** — `@voltro/cli`
|
|
562
|
+
|
|
563
|
+
`voltro db apply --plan` now checks the complete ordered operations and risk summary against the current plan before applying schema changes. Changed review contents refuse even when both schema fingerprints still match. JSON object-key order remains irrelevant.
|
|
564
|
+
|
|
565
|
+
The online index threshold is preserved during replanning under the migration lock and the index phase after commit. Use the same planning options for plan and apply.
|
|
566
|
+
- **Keep verified credential lifetime local to each RPC** — `@voltro/runtime`, `@voltro/cli`
|
|
567
|
+
|
|
568
|
+
Capture verified credential expiry on the first RPC and keep it isolated between concurrent calls on the same connection. Add `withRpcAuthMiddleware` to compose authentication before metadata capture, preserve explicitly unbounded credentials, and refuse a reversed framework pair before running handlers. Custom auth implementations retain their existing lifetime fallback.
|
|
569
|
+
- **RPC output encoding defects name the procedure and request** — `@voltro/runtime`
|
|
570
|
+
|
|
571
|
+
Both RPC transports log Exit/Die encoding failures as well as protocol defects, with rpcTag and requestId when available. JSON-value failures explain that Schema.Unknown output must contain JSON values; declare date codecs or explicitly serialize database dates. Typed failures and output data remain unchanged.
|
|
572
|
+
- **Service callers can open context transactions** — `@voltro/cli`
|
|
573
|
+
|
|
574
|
+
An authenticated service admitted by a procedure can use `ctx.transaction` outside an existing mutation. The shared development and production context retains the same service identity when opening its internal transaction, instead of refusing an undecided synthetic procedure. Nested work still joins that transaction and callback failures roll back the complete unit. Other procedures keep enforcing their own service audience; this grants no human session or additional scopes.
|
|
575
|
+
- **Preserve SQL causes in migration recovery diagnostics** — `@voltro/database`
|
|
576
|
+
|
|
577
|
+
Migration failures retain their original SQL cause and the progress, history-write or operation-ledger cleanup diagnostic. Getter-only SQL errors no longer replace these recovery details with a message-assignment TypeError; receipt validation and operation-ledger retention remain enforced.
|
|
578
|
+
- **SQLite-family write results retain declared column names beside bigint values** — `@voltro/database`
|
|
579
|
+
|
|
580
|
+
Explicitly alias ordinary columns in SQLite-family bigint row projections. Native Turso write results now retain declared IDs, tenant fields and timestamps instead of quoted expression labels. This preserves insert-ignore results and prevents the first successful storage quota reservation from being charged twice.
|
|
581
|
+
- **Keep internationalization provider effects stable** — `@voltro/i18n`
|
|
582
|
+
|
|
583
|
+
Keep the default internationalization error handler stable across provider renders. Releasing the SSR hydration clock no longer changes the translator context solely because a new error-handler closure was created, avoiding repeated effect-driven actions with unchanged inputs. Missing-translation suppression and explicit error-handler overrides are preserved.
|
|
584
|
+
- **Storage catalogue pages use correct singular and plural counts** — `@voltro/devtools-ui`
|
|
585
|
+
|
|
586
|
+
Display one file or Datei correctly in combined catalogue page counts while retaining the existing per-catalogue pagination semantics.
|
|
587
|
+
- **Persisted storage references belong to their configured instance** — `@voltro/plugin-storage`
|
|
588
|
+
|
|
589
|
+
Default persisted references, grants and upload intents are scoped to their configured storage instance. Wrong-instance reads and management cannot resolve another instance’s reference. Grant creation refuses missing and foreign-instance references before inserting a grant. Tenant quota remains shared and bucket metadata is preserved.
|
|
590
|
+
|
|
591
|
+
Existing metadata requires an explicit ownership assignment after the additive schema migration; application startup refuses unassigned rows. The `assignStorageReferenceInstancesInTransaction` helper runs in an after-schema file migration with serving replicas stopped. It preserves reference and upload identities, claims, quota and provider bytes, and refuses conflicting assignments. Independent providers sharing durable object generations still require separate lifecycle isolation.
|
|
592
|
+
- **Storage tokens expire at their signed deadline** — `@voltro/plugin-storage`
|
|
593
|
+
|
|
594
|
+
Native signed read grants and upload, finalize, resumable and multipart tickets now reject the exact signed expiry second. A zero-second lifetime no longer permits an upload, and expired tokens receive no extra second of access. Approved owner, tenant and access-policy bindings are unchanged.
|
|
595
|
+
- **Storage streams refuse missing objects before download headers** — `@voltro/plugin-storage`
|
|
596
|
+
|
|
597
|
+
StorageService acquires provider streams before constructing download responses. Missing files and provider acquisition failures retain their typed errors instead of failing only when the body is consumed. Supported providers still stream bodies without buffering them; providers without streaming support retain the existing buffered fallback.
|
|
598
|
+
- **Subscription refusals preserve their JSON wire representation** — `@voltro/protocol`
|
|
599
|
+
|
|
600
|
+
Subscription error frames preserve the JSON representation of tagged error instances before strict wire encoding. Live permission refusals retain their tags and fields instead of becoming JSON serialization defects. Non-serializable failures remain rejected.
|
|
601
|
+
- **Terminal subscription refusals remain visible after a snapshot** — `@voltro/client`
|
|
602
|
+
|
|
603
|
+
Queries retain their last valid data while exposing terminal handler or protocol failures through `error`, so an ended subscription is not silently presented as current. Refreshing the same query preserves its refusal until a successful delivery; transport interruption recovery and authentication identity resets keep their existing boundaries.
|
|
604
|
+
- **Keep component test recovery hooks consistent with the client** — `@voltro/testing`
|
|
605
|
+
|
|
606
|
+
The component test client withdraws preload seeds after a whole-cache refresh, including remounts, while retaining seeds across ordinary controlled transport changes. Visible error subscriptions, server health snapshots and action readiness use the controlled recovery surface. Disconnected actions fail before their handler starts; the harness does not automatically replay transport failures.
|
|
607
|
+
- **Recover client uploads with stable transfer identity** — `@voltro/client`
|
|
608
|
+
|
|
609
|
+
Retain the advertised native through-app upload operation across automatic retries. An optional caller-selected operationId supports recovery after a lost first mint acknowledgement or an explicit later UI retry with the same file and metadata. Reject a shared explicit identity for a multi-file batch before dispatch. Custom stores and other upload transports retain their separate contracts.
|
|
610
|
+
- **Through-app upload tickets resume one durable transfer** — `@voltro/plugin-storage`
|
|
611
|
+
|
|
612
|
+
Through-app upload tickets now use the native object operation when the application's DataStore and default metadata stores are bound. Replaying an issued ticket resumes one reference, keeps unfinished references out of listings, and recovers a retained intent after provider failure. Ordinary ticket responses expose the optional operation ID; supplying it with the same metadata in a fresh authorized request renews an expired ticket without another reference. Changed content under the same operation returns HTTP 409; upload and resumable responses retain declared provider error statuses. Custom metadata stores do not advertise native operation support.
|
|
613
|
+
- **Schedule initial SSR hydration as a React transition** — `@voltro/web`
|
|
614
|
+
|
|
615
|
+
Initial full-page SSR hydration uses React's transition scheduling while retaining server markup and React's first-interaction delivery. API/bootstrap initialization still completes before root creation; fresh client mounts and island strategies retain their existing paths.
|
|
616
|
+
- **Install component animation variables with their source** — `@voltro/cli`
|
|
617
|
+
|
|
618
|
+
`voltro ui add` and `voltro ui update` install component `cssVars` and CSS rules in one attributed, tracked local stylesheet. Tailwind animation utilities keep their delivered definitions; edited stylesheets retain the existing update protection. Unsafe CSS and remote resources remain refused before writes.
|
|
619
|
+
- **Preserve installed utilities in Git-tracked apps** — `@voltro/cli`
|
|
620
|
+
|
|
621
|
+
UI installation explicitly registers app sources with Tailwind so component utilities survive production builds after committing the project to Git.
|
|
622
|
+
- **Install exported theme geometry and local display fonts** — `@voltro/cli`
|
|
623
|
+
|
|
624
|
+
Install shared themes with exported geometry and component rules alongside both color modes. Validate the CSS before writing, keep local font imports before the rules, and retain exact Fontsource package pins and complete OFL licenses. Remote resources, escaping imports and unlicensed generated themes remain refused. Theme updates preserve edited stylesheets.
|
|
625
|
+
- **Keep the UI installer toolchain out of server artifacts** — `@voltro/cli`
|
|
626
|
+
|
|
627
|
+
Precompiled API and web servers keep the UI installer's TypeScript AST toolchain external. Source installation still uses the installed CLI dependencies; serving does not carry or load the compiler.
|
|
628
|
+
- **Keep UI installation React and renderer versions aligned** — `@voltro/cli`
|
|
629
|
+
|
|
630
|
+
When UI source pins React or React DOM, keep the application's existing counterpart on the same exact version, including the patch. Preserve its dependency group and refuse conflicting explicit registry pins before writing files. A renderer absent from the application is not added implicitly.
|
|
631
|
+
- **Accept valid dotted npm names in UI registry dependencies** — `@voltro/cli`
|
|
632
|
+
|
|
633
|
+
Allow valid dotted and underscored npm package names in Voltro UI registry dependencies, including highlight.js. Exact version pins and validation of unsafe names, URLs and filesystem paths remain required before installation writes files.
|
|
634
|
+
- **Apply selected UI themes after their base styles** — `@voltro/cli`
|
|
635
|
+
|
|
636
|
+
Keep tracked UI stylesheet imports in dependency order when installing a custom theme after Signal. The selected theme overrides its base palette while authored rules, unrelated imports and conditional imports remain intact. Repeated installation does not duplicate tracked imports.
|
|
637
|
+
- **Unchanged subscriptions confirm reconnect recovery** — `@voltro/runtime`, `@voltro/web`
|
|
638
|
+
|
|
639
|
+
A reconnect with no missed deltas now receives a fresh snapshot instead of a silent subscription adoption. This lets the client confirm that unchanged live queries have recovered and keeps subsequent updates live. Nonempty retained replays continue to deliver their missed deltas; existing authorization and tenant checks remain in place. No migration is required.
|
|
640
|
+
|
|
641
|
+
Browser recovery now stays in place in development as well as production. The development-only automatic page reload based on web-origin reachability has been removed: an API outage must not discard an open form's unsaved state.
|
|
642
|
+
- **Preserve creation metadata in default audited upserts** — `@voltro/runtime`
|
|
643
|
+
|
|
644
|
+
Default `upsert` and `upsertWithOutcome` preserve createdAt and createdBy on audited rows while stamping updatedAt and updatedBy. Insert defaults no longer leak into the conflict update projection. Explicit update lists, callbacks and updateValues remain deliberate overrides.
|
|
645
|
+
- **Preserve vendored source attribution in published packages** — `@voltro/ui-shadcn`
|
|
646
|
+
|
|
647
|
+
Package notices now include vendored source attribution from the package and its runtime dependencies. The styled UI kit ships the complete shadcn/ui MIT copyright and license separately from the framework license, alongside its source and build output. The publish stager retains these notices for consumers.
|
|
648
|
+
- **Audit hashes protect impersonation and stable row identity** — `@voltro/plugin-audit`
|
|
649
|
+
|
|
650
|
+
New audit entries use hash format 2, covering first-class impersonation, the derived scope key and pre-minted stable row identity. The nullable format column preserves verification of historical entries without rewriting their hashes; legacy coverage is counted separately. Unknown or downgraded markers fail verification. Apply the normal reviewed schema migration before production serving.
|
|
651
|
+
- **Require patched Vite for development and generated SDK builds** — `@voltro/cli`
|
|
652
|
+
|
|
653
|
+
The development toolchain and generated TypeScript SDK packages require Vite 8.3.3 or newer in the Vite 8 line, closing three launch-editor security advisories, including command injection on Windows (GHSA-587h-7wmj-h5pm, GHSA-698v-4r3x-3vcv and GHSA-r825-w8mm-5pxw).
|
|
654
|
+
- **Replace stale development module graphs after closing the live server** — `@voltro/cli`
|
|
655
|
+
|
|
656
|
+
API surface edits close the web server and optimizer before clearing dependency caches and starting a fresh process. Concurrent edits coalesce; failed shutdown or cache reset retries before failing visibly. Local replica publications use unique atomic temporary files.
|
|
657
|
+
- **Start workflow lease windows at the first engine dispatch** — `@voltro/workflow`
|
|
658
|
+
|
|
659
|
+
Workflow reservations retain their original finite lease duration when an ordinary or explicitly recovered start waits before engine dispatch. The first dispatch atomically moves that deadline forward using a fresh clock after execution-key preparation, so acknowledging a newly running start does not immediately free an already-expired slot. Later dispatch attempts, recovery-command replay and late acknowledgements do not renew the lease; normal finite crash-backstop expiry still applies after acknowledgement. Missing, bound, released or invalid reservation evidence refuses the first dispatch before engine I/O.
|
|
660
|
+
- **Record workflow authority refusal before application execution** — `@voltro/workflow`, `@voltro/runtime`, `@voltro/cli`
|
|
661
|
+
|
|
662
|
+
Development and production workflow hosts now record pre-body authority and row-filter refusals in the starter's remembered resident store. Engine failure and run history agree after credential revocation. The shared recording primitive uses existing transactional delivery and generation fences, terminal events, failure handoffs and lease cleanup without constructing an application context or running workflow code. Completed, cancelled and foreign-caller runs remain protected. Missing caller identities cannot allocate a tenant-owned run; storage errors fail visibly.
|
|
663
|
+
- **Waiting workflow starts preserve unchanged queue rows** — `@voltro/workflow`
|
|
664
|
+
|
|
665
|
+
Stop timestamp-only pending-row updates and change events when concurrency, singleton or pause conditions remain unchanged. Real deadline changes and durable arrival, retry, cancellation and admission transitions remain intact; coordinated polling continues to reconsider capacity.
|
|
666
|
+
- **Workflow inspection counts unknown acceptance as held capacity** — `@voltro/workflow`, `@voltro/cli`
|
|
667
|
+
|
|
668
|
+
Use the same lease predicate for admission and Flow inspection so an unreleased reservation with no execution acknowledgement remains visibly held after its timestamp expires. The inspector no longer reports spare capacity while those reservations block the pool.
|
|
669
|
+
|
|
670
|
+
### Internal (no consumer-facing effect)
|
|
671
|
+
|
|
672
|
+
- **Agent-flow run status union order in the API report** — `@voltro/plugin-ai-flows`
|
|
673
|
+
|
|
674
|
+
The API report lists the members of the run `status` union in a different order. The members are the same seven literals; no type changed.
|
|
675
|
+
- **Resolve the audited search SDK redirect client to its security patch** — `@voltro/plugin-search`
|
|
676
|
+
|
|
677
|
+
Resolve the workspace's optional search SDK redirect client to `follow-redirects` 1.16.1 or a later 1.x patch, retaining the major-version cap and an explicit security-patch cooldown exception. The dependency audit covers credential-header stripping fixes. This workspace resolution does not rewrite independently managed application lockfiles.
|
|
678
|
+
- **Reuse an identical production bundle within its integration suite** — `@voltro/cli`
|
|
679
|
+
|
|
680
|
+
Build the relocated HTTP and tracing probe once per test run while preserving separate deployment directories, processes, environments and assertions for every case.
|
|
681
|
+
- **Durable signal coverage before wait registration** — `@voltro/workflow`
|
|
682
|
+
|
|
683
|
+
Exercise the real workflow engine when a notification arrives after body entry and before a suspending wait registers.
|
|
684
|
+
- **Fixture typechecks regenerate their RPC groups first** — `@voltro/cli`
|
|
685
|
+
|
|
686
|
+
API fixture typechecks run the existing code generator before the compiler. A cached sibling build cannot leave an ignored RPC group using a previous Effect major, and typecheck does not race that sibling build's regeneration.
|
|
687
|
+
- **Clarify internal router context boundaries** — `@voltro/web`
|
|
688
|
+
|
|
689
|
+
Keep router context plumbing separate from the application-facing routing hooks.
|
|
690
|
+
- **Kernel schema fixture includes draft and revision reference stages** — `@voltro/cms`
|
|
691
|
+
|
|
692
|
+
Advance the schema fixture version after reconciling the existing kernel test with staged references, revision identity columns and scoped reference indexes. Keep the existing native bulk-delivery test aligned with the two authorization fences around a shared read. Runtime schema, authorization and migration behavior are unchanged.
|
|
693
|
+
- **Use dependency-free ULID generation** — `@voltro/database`
|
|
694
|
+
|
|
695
|
+
ULID generation uses the maintained `ulid` package. The `ulidx` and `layerr` dependency chain is removed; the 26-character Crockford-base32 ID format, timestamp encoding and cryptographically secure randomness remain unchanged.
|
|
696
|
+
- **Measure web reference budgets across transitive static imports** — `@voltro/web`
|
|
697
|
+
|
|
698
|
+
Measure the web reference budget through transitive static ESM imports as well as preload hints, with cycle/re-export regression coverage and stricter retained ceilings.
|
|
699
|
+
- **Keep full test coverage while reducing pipeline overhead** — `@voltro/build`, `@voltro/cli`, `@voltro/runtime`, `@voltro/plugin-storage`
|
|
700
|
+
|
|
701
|
+
Parallelize cold builds and independent checks, verify transferred artifacts, balance exhaustive CLI test groups, and retire exclusive test services after their consumers finish. Reuse identical fixture builds and source dumps within a suite, observe process termination directly, and preserve diagnostic failures.
|
|
702
|
+
- **Preserve checked declaration inference** — `@voltro/build`
|
|
703
|
+
|
|
704
|
+
Keep full checking in the declaration emitter to preserve writable inferred return types. Retain native fail-fast validation and reuse checking only in API Extractor; add a consumer regression against the built flow executor.
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## [0.85.0] — 2026-10-10
|
|
709
|
+
|
|
710
|
+
### ⚠ BREAKING
|
|
711
|
+
|
|
712
|
+
- **Scripted planner responses must use the strict JSON envelope** — `@voltro/plugin-ai-flows`, `@voltro/cli`
|
|
713
|
+
|
|
714
|
+
Since 0.84.0, the final agentic planner model response uses the closed `{ "json": "<serialized deliverable object>" }` envelope. Update scripted model responses, recorded fixtures and custom planner adapters to `JSON.stringify({ json: JSON.stringify(deliverable) })`; direct deliverable responses fail. The engine still returns and stores the original deliverable object. This corrects the earlier classification as a fix; the model response contract changed. `voltro update` reissues the review for applications already on 0.84.0.
|
|
715
|
+
- **CMS: experiments planned on a release, and version B for one section** — `@voltro/cms`
|
|
716
|
+
|
|
717
|
+
`planExperiment` binds an experiment to a draft or scheduled release that publishes its page: it stays `planned` until the release goes live, then starts on the revision the release published (replacing one still running there, `stopReason: 'replaced'`), and a rollback of that release stops it (`stopReason: 'rolledBack'`). The release runs this through `afterPublish` hooks (`ReleaseHook`, `PublishReleaseOptions`), which `publishRelease`, `publishDueReleases` and `rollbackRelease` now take — pass `experimentReleaseHook`. Version B may also replace a text prop of one placed component (`sections[<instanceId>].heading`; `experimentOverrideKeys` lists them). `Experiment.status` gains `'planned'` and `Experiment.startedAt` is `Date | null`.
|
|
718
|
+
|
|
719
|
+
**`voltro update` carries you across this** — codemod `0.85.0/04_cms-experiments-planned-with-a-release`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
720
|
+
- **Live row patches omit unchanged ID order and reuse subscription indexes** — `@voltro/protocol`, `@voltro/client`, `@voltro/runtime`, `@voltro/api-client`, `@voltro/cli`, `@voltro/voltro`
|
|
721
|
+
|
|
722
|
+
`RowPatch.order` is now an ID array or `null`: `null` preserves the previous order, while arrays describe changed membership or ordering. Protocol version is 2. Upgrade APIs, browser clients and generated SDK runtimes together; custom consumers must handle `null` or use `applyRowPatch`. `voltro update` supplies the review instructions. Subscription-owned indexes avoid rebuilding the ID map for each delta. Waiting transports apply each delta once, including cell merges.
|
|
723
|
+
- **What Effect 4 changes without renaming anything** — `@voltro/runtime`, `@voltro/protocol`, `@voltro/workflow`, `@voltro/database`, `@voltro/client`, `@voltro/web`, `@voltro/testing`, `@voltro/logger`, `@voltro/data-transfer`, `@voltro/env`, `@voltro/plugin-sentry`, `@voltro/plugin-datadog`, `@voltro/plugin-logship`
|
|
724
|
+
|
|
725
|
+
`Schema.Date` is `DateFromSelf`: a string is no longer decoded, use `Schema.DateFromString` where one arrives. A layer used twice inside one layer graph builds once — per build, not per process; two separate `Effect.provide` calls are two builds (`Layer.fresh` opts out). `Equal.equals` is structural for plain objects and arrays, so `Data.struct` and `Data.array` are gone. An error class without a `message` field reports an empty message: give every `TaggedError` a `message` field or an `override get message()`. `Cause` is flat (`cause.reasons`), `yield* Option.x` is a type error (`Effect.fromOption`), `Effect.fork` binds the child to its parent and a daemon is `Effect.forkDetach`, a process stays alive while fibers are open, `Stream.async` is `Stream.callback`, `Config` is schema-based (`Config.String("NAME")`), `FiberRef` is `Context.Reference`, `FiberId` is a number in log and trace fields, and `Runtime<R>` is gone (`Effect.context<R>()`). A span without a parent reports `root: true`; a detached trace needs an explicit `parent`. `NumberFromString` decodes `"abc"` to `NaN` instead of failing — add `Schema.check(Schema.isFinite())` where you relied on the refusal. A production web image loads `effect` three times (start bundle, precompiled `app.config`, SSR bundle); the shipped `effect` patch keys the default `MetricRegistry` on the process, so the inspect endpoints and the Prometheus exporter see every copy's series. Effect 4 histogram buckets are the declared boundaries only; the catch-all is the sample's `count`. The codemod prints these as written steps against your project.
|
|
726
|
+
|
|
727
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
728
|
+
- **The framework runs on Effect 4** — `@voltro/ai`, `@voltro/api-client-effect`, `@voltro/api-client`, `@voltro/cache`, `@voltro/changelog`, `@voltro/client`, `@voltro/cli`, `@voltro/cms`, `@voltro/content`, `@voltro/database`, `@voltro/data-transfer`, `@voltro/datetime`, `@voltro/desktop`, `@voltro/devtools`, `@voltro/devtools-ui`, `@voltro/env`, `@voltro/i18n`, `@voltro/integration-http`, `@voltro/kv`, `@voltro/local-first`, `@voltro/logger`, `@voltro/mcp`, `@voltro/plan`, `@voltro/plugin-ai-flows`, `@voltro/plugin-analytics-postgres`, `@voltro/plugin-atlassian`, `@voltro/plugin-audit`, `@voltro/plugin-auth-auth0`, `@voltro/plugin-auth-clerk`, `@voltro/plugin-auth-kinde`, `@voltro/plugin-auth-oidc`, `@voltro/plugin-auth`, `@voltro/plugin-auth-social`, `@voltro/plugin-auth-supabase`, `@voltro/plugin-auth-workos`, `@voltro/plugin-billing`, `@voltro/plugin-broadcast`, `@voltro/plugin-cdc-out`, `@voltro/plugin-clickhouse`, `@voltro/plugin-comments`, `@voltro/plugin-datadog`, `@voltro/plugin-deactivation`, `@voltro/plugin-duckdb`, `@voltro/plugin-flags`, `@voltro/plugin-governance`, `@voltro/plugin-licensing`, `@voltro/plugin-logship`, `@voltro/plugin-mail`, `@voltro/plugin-moderation`, `@voltro/plugin-multitenancy`, `@voltro/plugin-notifications`, `@voltro/plugin-openapi`, `@voltro/plugin-postgis`, `@voltro/plugin-posthog`, `@voltro/plugin-presence`, `@voltro/plugin-prometheus`, `@voltro/plugin-queue`, `@voltro/plugin-ratelimit`, `@voltro/plugin-rbac`, `@voltro/plugin-releases`, `@voltro/plugin-row-history`, `@voltro/plugin-scim`, `@voltro/plugin-search`, `@voltro/plugin-sentry`, `@voltro/plugin-soft-delete`, `@voltro/plugin-sso-saml`, `@voltro/plugin-storage`, `@voltro/plugin-tinybird`, `@voltro/plugin-webhooks`, `@voltro/protocol`, `@voltro/react-native`, `@voltro/runtime`, `@voltro/serverless`, `@voltro/shared`, `@voltro/sql-mssql`, `@voltro/sql-mysql`, `@voltro/sql-postgres`, `@voltro/sql-sqlite`, `@voltro/sql-turso`, `@voltro/testing`, `@voltro/ui`, `@voltro/ui-shadcn`, `@voltro/voltro`, `@voltro/web`, `@voltro/workflow`
|
|
729
|
+
|
|
730
|
+
Every package depends on `effect` 4.0.1 exactly, and the packages that merged into it (`@effect/platform`, `@effect/sql`, `@effect/rpc`, `@effect/cluster`, `@effect/workflow`, `@effect/experimental`) are modules of `effect` now: `effect/sql`, `effect/rpc`, `effect/cluster`, `effect/workflow`, `effect/http`. Public types render in Effect 4 terms — `Schema.Codec` instead of `Schema.Schema`, `Context.Service` instead of `Context.Tag`, `Result` instead of `Either`, `Schema.optional` instead of `Schema.optionalWith`. Migration: pin `effect` to 4.0.1, remove the merged packages from your dependencies, keep your SQL driver (`@effect/sql-sqlite-node`, `@effect/sql-pg`, `@effect/sql-mysql2` or `@effect/sql-mssql`) at 4.0.1, and add `ESNext.Disposable` to your tsconfig `lib`. The codemod rewrites imports, renamed members and the constructors that take an array now (`Schema.Union([A, B])`, `Schema.Literals(['a', 'b'])`, `Schema.Record(key, value)`, `Context.Service<Self, Shape>()(id)`, `Schema.check(Schema.isMinLength(1))`), and leaves a `// TODO(voltro-migration):` line above every site that needs a decision.
|
|
731
|
+
|
|
732
|
+
**`voltro update` carries you across this** — codemod `0.85.0/90_effect-4`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
733
|
+
- **Histogram boundaries are plain arrays and labels are attributes** — `@voltro/runtime`, `@voltro/plugin-prometheus`, `@voltro/plugin-datadog`
|
|
734
|
+
|
|
735
|
+
`histogramMetric` takes a `ReadonlyArray<number>`; `@voltro/runtime` exports `exponentialBoundaries` and `linearBoundaries` instead of re-exporting `MetricBoundaries`. Effect 4's `linearBoundaries` builds `start + i * width` and drops non-positive values, which is not the series `MetricBoundaries.linear` produced — rebuild your boundaries explicitly where the exact buckets matter. Labels are set with `Effect.provideService(Metric.CurrentMetricAttributes, { ... })` instead of `Effect.tagMetrics`; a snapshot is `{ id, type, description, attributes, state }`.
|
|
736
|
+
|
|
737
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
738
|
+
- **Postgres workflow runners lock shards in a new key space — no rolling deploy** — `@voltro/workflow`, `@voltro/sql-postgres`, `@voltro/cli`
|
|
739
|
+
|
|
740
|
+
Effect 4's cluster storage takes each postgres shard lock as a two-key advisory lock under its own namespace; Effect 3 took one `bigint` key. A runner on 0.84 and a runner on 0.85 against the same database therefore cannot see each other's locks and could both own a shard. Stop every runner of the old version before the first 0.85 runner starts; a rolling deploy across this version is not safe on postgres. MySQL and MariaDB keep their lock names. The stalled-run diagnostics (`run-stalled`, the cluster panel) read the new key space, so a held shard names its holder again.
|
|
741
|
+
|
|
742
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
743
|
+
- **Authorize tenant-private browser upload policies** — `@voltro/protocol`, `@voltro/client`, `@voltro/plugin-storage`, `@voltro/cli`
|
|
744
|
+
|
|
745
|
+
`useUpload` accepts an `access` policy for private uploads. The server authorizes requests and visibility through an Effect `uploadAccess` function before issuing a ticket or accepting the direct base64 upload RPC; an unconfigured or static default rejects requested policies. The approved policy is signed into through-app, presigned, resumable and multipart tickets and kept through finalization. A tenant-scoped read policy lets authorized colleagues use one stored document without publishing it or copying its bytes to another ref.
|
|
746
|
+
|
|
747
|
+
Custom ticket issuers must pass `access` explicitly to share private uploads; receivers no longer apply a mutable server default. Previously issued upload tokens are refused. Coordinate API rollout and restart unfinished upload sessions; existing stored objects and grants remain intact. The upgrade note covers custom issuers and cleanup of abandoned staged uploads.
|
|
748
|
+
|
|
749
|
+
Private tenant-owned objects now refuse a caller in another tenant before owner, policy or explicit grant checks, including an owner who switches tenant. Custom access-check subjects must carry their verified current tenant. Global references retain policy checks; public objects remain publicly readable.
|
|
750
|
+
|
|
751
|
+
**`voltro update` carries you across this** — codemod `0.85.0/03_storage-upload-ticket-access`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
752
|
+
- **Bound reaction admission work and batch reference cleanup** — `@voltro/runtime`, `@voltro/cli`
|
|
753
|
+
|
|
754
|
+
Row-set ReBAC filtering prepares relation tuples per invocation while keeping tenant checks and grant revocation fresh. Reaction rate admission uses a transaction-owned bucket cursor and unique slots, preserving fleet caps without restarting slot searches; active windows remain protected from retention cleanup. Custom `ReactionRunDeps` implementations replace `claimRateSlot` with `claimRate(key, limit, expiresAt)`. Custom `PluginRefStore` adapters use predicate-scoped `updateMany`/`deleteMany`; normal reference cleanup batches writes, retains soft-delete and tenant semantics, and drains bounded concurrent work at shutdown. The local fallback avoids copying saturated timestamp windows and reclaims idle partitions using their last declared window.
|
|
755
|
+
|
|
756
|
+
Rate keys use SQL-safe JSON encoding, also supporting PostgreSQL and embedded delimiters. Before changing from the former encoding, stop reaction producers and wait at least the longest configured rate window; upgrade the fleet together after applying the normal cursor-column migration. Mixed encodings must not run in an active window.
|
|
757
|
+
|
|
758
|
+
**`voltro update` carries you across this** — codemod `0.85.0/08_reaction_rate_and_reference_cleanup`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
759
|
+
- **Completed memory streams have an explicit replay window** — `@voltro/ai`, `@voltro/cli`
|
|
760
|
+
|
|
761
|
+
Completed memory-stream payloads expire after seven days by default. Set a longer finite `retentionMs` when needed. Active readers defer cleanup; producer-election tombstones remain until the owner disposes the permanently non-reusable store. Replay reads are paged, local persistence wakes readers, remote polling backs off, natural source chunks can persist in bounded bulk writes, and done-aware collection uses bulk deletes. Custom stores may implement `appendMany` and honor the optional read limit.
|
|
762
|
+
|
|
763
|
+
Redis replay verifies list positions and falls back to bounded filtering after sequence gaps, preserving later events and terminal outcomes.
|
|
764
|
+
|
|
765
|
+
**`voltro update` carries you across this** — codemod `0.85.0/07_memory-stream-retention`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
766
|
+
- **Fetch bounded SCIM pages and aggregate totals** — `@voltro/plugin-scim`, `@voltro/cli`
|
|
767
|
+
|
|
768
|
+
SCIM lists now fetch a bounded, id-ordered page and a count aggregate instead of loading every matching user or group. HTTP lists default to 100 rows and cap requested counts at 1000; count zero still returns the full matching total. Custom ScimStore adapters must implement pageUsers and pageGroups, returning rows and totalResults. The update codemod provides the required migration steps.
|
|
769
|
+
|
|
770
|
+
**`voltro update` carries you across this** — codemod `0.85.0/06_scim-store-pages`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
771
|
+
- **Store transactions share one key across every client and driver** — `@voltro/database`, `@voltro/sql-sqlite`, `@voltro/sql-postgres`, `@voltro/sql-mysql`, `@voltro/sql-mssql`, `@voltro/sql-turso`, `@voltro/cli`
|
|
772
|
+
|
|
773
|
+
Effect 4 keys the transaction connection per `SqlClient` instance, so a statement through a second client (a plugin runtime, a second layer build) would run outside the store's transaction. `@voltro/database` exports `SHARED_TRANSACTION_SERVICE`, every dialect layer passes it to its driver, and the drivers for sqlite-node, mysql2 and mssql accept it through pnpm patches. `voltro create-project` writes the patch files and `patchedDependencies` entries into the workspace root it creates; an existing project runs `voltro add patches` once (the `effect` patch plus the driver patch of every dialect its apps declare) and commits the `patches/` files and entries — a framework package install alone cannot apply them, and `voltro dev` and `voltro serve` warn while they are missing. The sqlite driver opens transactions deferred instead of `BEGIN IMMEDIATE` (two connections in one process no longer deadlock), the postgres client runs unnamed statements (prepared statements went stale after `ALTER TABLE`) and opens its `LISTEN` connection outside the pool as the boot line promises, and `xid`, `xid8` and `pg_snapshot` columns decode. `validatePatch` takes a `Schema.Struct` only; `extractDbCause` and `causeBranches` take an `Exit` or `Cause`; `database/sql` exports `splitStatements` for multi-statement bootstrap SQL and `onTransaction` for work scoped to the surrounding transaction; the postgres type registry moved from `@voltro/database` to `@voltro/sql-postgres`.
|
|
774
|
+
|
|
775
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
776
|
+
- **Test runners take no abort signal and the test clock moves** — `@voltro/testing`
|
|
777
|
+
|
|
778
|
+
`runPromise` and `runPromiseExit` from `@voltro/testing/client` follow the Effect 4 `ManagedRuntime` signature and take no `signal` option; interrupt the fiber instead. `TestClock` comes from `effect/testing/TestClock`, `TestContext` is `Layer.mergeAll(TestConsole.layer, TestClock.layer())`, and the test event bus reports a `Result` (`Result.isFailure`, `.failure`, `.success`).
|
|
779
|
+
|
|
780
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
781
|
+
- **Schema.Uint8Array no longer decodes a number array** — `@voltro/protocol`
|
|
782
|
+
|
|
783
|
+
Under Effect 4 `Schema.Uint8Array` is the identity codec: it accepts a `Uint8Array` and nothing else, so a procedure field declared with it no longer accepts the number array a JSON client sends. Declare binary fields as `Schema.Uint8ArrayFromBase64` — the form the framework already uses for binary columns — or spell out a number-array transformation with `Schema.decodeTo`. The codemod marks every `Schema.Uint8Array` with a `// TODO(voltro-migration):` line.
|
|
784
|
+
|
|
785
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
786
|
+
- **RPC exits carry a reason list and void payloads are null on the wire** — `@voltro/protocol`, `@voltro/runtime`, `@voltro/client`, `@voltro/web`, `@voltro/api-client`, `@voltro/api-client-effect`, `@voltro/cli`, `@voltro/plugin-openapi`
|
|
787
|
+
|
|
788
|
+
An encoded exit's `cause` is a list of reasons (`[{ _tag: 'Fail', error }]`, `Die`, `Interrupt`) instead of a cause tree, an unroutable tag answers as an exit with a `Die` reason, `Schema.Void` input is `null` instead of `{}`, and a stream rpc folds its error into the stream schema (`rpc.errorSchema` is `Never`; read `success`/`error` from the stream schema). Shape digests are computed from the Effect 4 AST, so every client logs the drift warning once after the upgrade; decoding error texts read `Missing key` and `is unexpected`. `formField()` returns `{ voltroField }` instead of `jsonSchema["x-voltro"]`, the `ParseResult` type re-export is `SchemaError`, and search-param defaults are `X.pipe(Schema.withDecodingDefaultType(Effect.succeed(d)))`. A raw `effect/rpc` client keeps a dotted tag as one flat key (`client['todos.list'](...)`). JSON Schema output (OpenAPI, SDK generation, form views) emits plain `number` and closed objects; generated SDK clients expose `Context.Service<ApiClient, ApiClientService>()` with a `Layer.effect`.
|
|
789
|
+
|
|
790
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
791
|
+
- **Workflow definitions are callable and journal exits are reason lists** — `@voltro/workflow`, `@voltro/cli`, `@voltro/plugin-webhooks`
|
|
792
|
+
|
|
793
|
+
A `workflow()` definition is a function in Effect 4: a `typeof x === 'object'` guard or an object spread drops it silently (no discovery, the body never runs). Test with `isCarrier` or the exported type id, never by shape. A journal entry's exit is the exported `JournalExit` with a reason list, `failureMessage` renders it, and `poll` answers an `Option`. `DurableRateLimiter` has no Effect 4 counterpart: `rateLimit()` consumes `effect/persistence/RateLimiter`, which the boot layer provides; `RateLimitOptions` is exported. Cluster routing sends a message to the runner that registered the entity type, so a runner booted without the workflow layer no longer fails the dispatch — it never sees it.
|
|
794
|
+
|
|
795
|
+
**`voltro update` carries you across this** — codemod `0.85.0/91_effect-4-semantics`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.85.0).
|
|
796
|
+
|
|
797
|
+
### Added
|
|
798
|
+
|
|
799
|
+
- **CMS: AI candidates for whole documents, adopted unit by unit** — `@voltro/cms`
|
|
800
|
+
|
|
801
|
+
`proposeCandidate` keeps a model's proposal for several fields beside the draft, with the revision it was made against and the AI operation behind it; `candidateUnits` shows it as units (a field, a changed section, an added section); `adoptCandidate` saves any subset as a new draft revision and settles the operation as applied; `discardCandidate` and `candidatesFor` complete it. Nothing publishes by itself. Register `cmsAiCandidatesTable`.
|
|
802
|
+
- **CMS: bulk into a release, and bulk runs that continue where they stopped** — `@voltro/cms`
|
|
803
|
+
|
|
804
|
+
`BulkAction` gains `addToRelease`: each ready item's pinned revision goes into one draft release (`releaseClosed` once it is not a draft). `startBulkRun` records a run (`cmsBulkRunsTable`); `continueBulkRun` does the next items one by one, saving each result before the next, and continues at the first item without one after an interruption; `unfinishedBulkRuns` and `bulkRunState` read runs.
|
|
805
|
+
- **Cache identity compiler for delivery behind shared caches** — `@voltro/cms`
|
|
806
|
+
|
|
807
|
+
`compileCacheIdentity(contract, request, { checkpoint, tags })` computes one canonical cache identity for a delivery answer — typed, sorted, allowlisted query parameters; bounded locale, audience and consent dimensions; the publication checkpoint — and from the same computation the `Vary` header, the purge selector and an explanation. Requests carrying a credential, writes, and paths without a single canonical form are never shared-cacheable; a contract past its `cardinalityBudget` is refused. `edgeHeaders(provider, identity, lifetime)` writes the headers for Cloudflare (`Cache-Tag`), Fastly (`Surrogate-Key`) or any `Cache-Tag` cache; `sharedCacheable` re-checks the origin's answer.
|
|
808
|
+
- **CMS: collections and brand portals with capped external downloads** — `@voltro/cms`
|
|
809
|
+
|
|
810
|
+
`createCollection`, `addToCollection`, `removeFromCollection`, `collectionItems` and `listCollections` keep curated sets of documents. `createPortal` makes a link to one (only the token's hash is stored, expiry ≤ 90 days, optional `maxDownloads`); `resolvePortal` and `recordPortalDownload` serve it to someone without an account — the count is a conditional update, so concurrent downloads never pass the cap. `revokePortal` ends a link at once. Register `cmsCollectionsTable`, `cmsCollectionItemsTable` and `cmsCollectionPortalsTable`.
|
|
811
|
+
- **CMS: components say what they are for; the picker finds them by job** — `@voltro/cms`
|
|
812
|
+
|
|
813
|
+
`defineComponent` accepts `description` and `jobs` (the editorial jobs a component serves). `<ContentForm>`'s component picker groups by job, searches name, description and jobs, says when nothing matches and shows the chosen component's description. New form strings: `findComponent`, `noComponentFound`, `otherComponents`. Neither field is part of the component's `fingerprint`.
|
|
814
|
+
- **CMS: component slots, variants, fixtures and migrations** — `@voltro/cms`
|
|
815
|
+
|
|
816
|
+
A component prop may hold components (a slot): every level gets stable `instanceId`s and fingerprints on save. `defineComponent` accepts `variants` (named presets the picker offers as "Card — compact"), `fixtures` (example props, checked against the component's props when a content type uses it — `componentExampleViolations`) and `migrations` keyed by an earlier definition's fingerprint (`componentFingerprint`), applied on save. `staleComponents(type, values)` lists instances whose definition changed with no migration from them.
|
|
817
|
+
- **CMS: consent vendors and the reader's choice as a browser-enforced policy** — `@voltro/cms`
|
|
818
|
+
|
|
819
|
+
`ConsentPurpose.vendors` names the third parties a purpose lets in — `id`, `origins`, `storage`, `dataUse` — separately from the purpose. `consentContentSecurityPolicy` turns a reader's choices into a Content-Security-Policy whose `frame-src` admits only this origin and the granted vendors (set it from a web middleware); `consentedOrigins` and `checkConsentPurposes` (refuses an origin that is not exactly `https://host`) go with it. Exported from `@voltro/cms` and `@voltro/cms/web`.
|
|
820
|
+
- **CMS: cutover readiness — issues, adoption, criteria, go/fallback ledger** — `@voltro/cms`
|
|
821
|
+
|
|
822
|
+
`evaluateCutover` measures criteria against thresholds; `recordCutoverIssues` / `resolveCutoverIssue` keep a migration source's open issues until each is closed with a reason; `cutoverAdoption` counts who saved revisions of migrated documents since import; `decideCutover` records `go` (refused while a criterion fails) or `fallback` with the criteria as they stood (`cutoverDecisions`). Register `cmsCutoverIssuesTable` and `cmsCutoverDecisionsTable`.
|
|
823
|
+
- **CMS: delivery checks — a publish is done when its public address shows it** — `@voltro/cms`
|
|
824
|
+
|
|
825
|
+
`requestDeliveryCheck` records, per publish or take-down, the public address that must now answer differently; `runDeliveryChecks` reads pending addresses through a reader function the app supplies (through any CDN, without credentials) and marks each `observed` when the address shows the revision (or `gone`) or `stale` after the deadline; `deliveryChecks` lists them. A newer change supersedes a pending check. Register `cmsDeliveryChecksTable`.
|
|
826
|
+
- **CMS: design tokens and actions as component props** — `@voltro/cms`
|
|
827
|
+
|
|
828
|
+
`defineDesignTokens` declares the token groups a design system offers; `Schema.Token(tokens, group)` is a prop that names one of them and `Schema.Action({ id: label })` one that names an action the renderer implements. Both are closed sets checked at save like `Schema.Literal`, and the form shows what each value means (`FieldSchema.literalLabels`). Exported from `@voltro/cms` and `@voltro/cms/web`.
|
|
829
|
+
- **A download block in rich text, its file from the media library** — `@voltro/cms`
|
|
830
|
+
|
|
831
|
+
A rich-text field that names an asset type for files (`Schema.RichText({ files: 'asset' })`) takes a download block: `{ type: 'file', asset, label }` — a file from the media library and the text its link shows (required). It is offered in the editor only where the field allows files and the app can pick one, through the same picker (and selection intent) as an image block; it is an edge to its asset, its link text is a translation unit, and `RichDocumentView` shows it as a download through `fileHrefOf(asset)` — left out when the file is not live, never a dead link. A profile's default blocks now include `file`.
|
|
832
|
+
|
|
833
|
+
A `RichDocumentPicker` learns what its choice is for (`role`: `image`, `file`, `link`, `contact`), so one picker for the asset type can open the media library for images or for files; a `SelectionIntent` carries the wanted `kind` of the picked type (`image`, `file`) for the tool to show only those.
|
|
834
|
+
- **edgePurgeRequests purges pages by address too** — `@voltro/cms`
|
|
835
|
+
|
|
836
|
+
`edgePurgeRequests(provider, config, tags, urls?)` takes the addresses of answers that carry no tags — the HTML pages a reader gets — beside the tags: Cloudflare purges them as `files` (30 a call), Fastly with one URL purge each, and a webhook receives `{ tags, urls }`. An address that is not an absolute http(s) URL is left out. Calls without `urls` are unchanged.
|
|
837
|
+
- **Purge requests for Cloudflare, Fastly or a webhook, by cache tag** — `@voltro/cms`
|
|
838
|
+
|
|
839
|
+
`edgePurgeRequests(provider, { target, token }, tags)` builds the requests a CDN takes to drop every answer carrying the tags — Cloudflare's tag purge (30 tags per call, larger sets split), Fastly's surrogate-key purge, or an `https` webhook taking `{ tags }`. Nothing is sent; tags a header could not carry are left out.
|
|
840
|
+
- **CMS: independent copies with their source and revision** — `@voltro/cms`
|
|
841
|
+
|
|
842
|
+
`copyDocument` makes a new document from another's current draft — every value, rights and references included — and records the source and the revision it was made from (`cms_copies`); `copyOrigin` and `copiesOf` read it back. A copy goes its own way afterwards, unlike a reference. Register `cmsCopiesTable`.
|
|
843
|
+
- **CMS: masked inheritance, effective values and their origin, release pins** — `@voltro/cms`
|
|
844
|
+
|
|
845
|
+
An inherited tree field may name a `mask` — an optional Boolean field that hides the binding on purpose for the page and the pages below (`saveDraft` refuses a page that sets both: `maskedAndSet`; a locked field has no mask). `inheritedValue` now also answers `how` (`own`, `ancestor`, `masked`, `none`) and takes the `mask`; `effectiveInheritance` answers every inherited field of a page with its origin, lock, mask and what a reset brings back. `pinReleaseInheritance` records on a release item what the page inherited as the release went live (new nullable `inherited` column on `cms_release_items`).
|
|
846
|
+
- **CMS: licence terms — from, territories, channels, purposes; rights ending soon** — `@voltro/cms`
|
|
847
|
+
|
|
848
|
+
`availability` accepts `from`, `territories`, `channels` and `purposes`. A right that has not started yet refuses the publish (`notYet`); `unavailableAmong(store, ids, scope, now, { territory, channel, purpose })` withholds a document licensed elsewhere (`territory`, `channel`, `purpose`), checking only what the caller names. `expiringAmong(store, scope, days)` lists rights that end soon. `ReferenceUnavailable`'s `reason` gains the new values.
|
|
849
|
+
- **A rich text's link picker learns its block; visual selection as offsets** — `@voltro/cms`
|
|
850
|
+
|
|
851
|
+
A paragraph's link picker now receives `target: { field, blockId }` like an image block's picker, so a link target can be chosen in another tool through a selection intent. `textSelectionIn(element)` reads the selected text of a block's visual surface as the character offsets a selection intent's `target.selection` takes. Each block's group carries `data-cms-block-id`.
|
|
852
|
+
- **CMS: migration stages with owners and exit gates, record ledger, cutover plan** — `@voltro/cms`
|
|
853
|
+
|
|
854
|
+
A migration runs through `MIGRATION_STAGES` (inventory, mapping, dry run, reconciliation, delta, cutover, fallback, editor acceptance): `setStageOwner` names an owner, `passStage` passes an exit gate — only the owner, with a note and optional evidence link, in order, and while the stage's measured `gate` holds — and `reopenStage` reopens a stage and every later one. `recordMigrationRecords` keeps one ledger row per source record (target, transformation, conflict, loss, result; reruns update, never double) and `migrationLedger` counts and lists them. `decideCutover` now needs a `CutoverPlan` for `go` — freeze or dual-write window, routing, redirects, cache purge and a rollback window of at most `MAX_ROLLBACK_DAYS` — and refuses with `noPlan` (`cutoverPlanProblem` names why). Register `cmsMigrationStagesTable` and `cmsMigrationRecordsTable`; `MIGRATION_STAGES` is also exported from `@voltro/cms/web`.
|
|
855
|
+
- **CMS: quality rules form policy packs per site and language; checks see when** — `@voltro/cms`
|
|
856
|
+
|
|
857
|
+
A quality rule may name its `pack` and the `sites` / `locales` it applies in; elsewhere it neither finds nor blocks, and no exception can be asked for it there. `check(values, { now, scope })` receives when and where it runs, so a claim whose evidence has run out blocks from that day on. `qualityFindings(type, values, { now, scope })` takes that context optionally (without it every rule is checked now, as before), and findings carry their `pack`. `ruleApplies(rule, scope)` is exported.
|
|
858
|
+
- **CMS: typed presence anchors** — `@voltro/cms`
|
|
859
|
+
|
|
860
|
+
`PresenceAnchor` names where someone is — a view, a field, a placed component, one of its props (a slot), a selection or an image region — without any text or time. `presenceAnchorOf` resolves a focused `ContentForm` control to its anchor (the form now marks each placed component with `data-cms-instance`), and `parsePresenceAnchor` accepts another client's anchor only in the declared shapes. Both are also in `@voltro/cms/web`.
|
|
861
|
+
- **CMS: reference graph per perspective and revision; replace references everywhere** — `@voltro/cms`
|
|
862
|
+
|
|
863
|
+
`documentGraph` explains a document both ways in a perspective — published, drafts or one revision as saved: what it references and which components it places (`componentsOf`), what uses it (live edges, or drafts via `draftUses`), its address and former addresses, and the releases carrying it. `revisionsReferencing` lists every saved revision that pointed at a document. `planReferenceReplacement` and `replaceReferences` point every draft that uses a document at another one of the same type — a new draft revision each against the planned revision, in its own transaction (call it from an action or a schedule, not inside a mutation), a result per draft with its detail, nothing published; `withReferenceAt` is the pure path step.
|
|
864
|
+
- **A release's checkpoint across the addresses of its entries** — `@voltro/cms`
|
|
865
|
+
|
|
866
|
+
`releaseCheckpoint(store, releaseId)` answers whether a published release reached readers: for each entry, the delivery check requested when the release went out — a publish answering with the revision the release pinned, a take-down no longer answering — with `reached`, `checked` and `complete`. An entry without a public address is named `unchecked` and is not counted as reached.
|
|
867
|
+
- **CMS: reviewer suggestions on a workflow step, and overdue steps for reminders** — `@voltro/cms`
|
|
868
|
+
|
|
869
|
+
`suggestOnStep` records a reviewer's suggested change on an open workflow step (decision `suggested`, with the `candidateId` of a candidate the author adopts or leaves); candidates carry `suggestedBy` for a person's suggestion. `overdueSteps(store, since, now)` lists open steps that fell due in a window, for reminders a schedule sends.
|
|
870
|
+
- **CMS: review workflows as a versioned decision graph** — `@voltro/cms`
|
|
871
|
+
|
|
872
|
+
`review.workflow` (`defineReviewWorkflow`) turns a type's review into steps with an approver scope, `after` dependencies, a `when` condition, an SLA and an evidence requirement. `startWorkflow` pins an instance to the exact revision and workflow version; `decideStep` (approve, request changes with a note) and `delegateStep` record decisions; `workflowState` / `latestWorkflow` derive each step's status, due time and overdue flag. The publish gate then needs an instance that approved the revision (`approvedByWorkflow`). Register `cmsWorkflowInstancesTable` and `cmsWorkflowDecisionsTable`.
|
|
873
|
+
- **Rich-text field profiles, components in text, track changes, typing together** — `@voltro/cms`
|
|
874
|
+
|
|
875
|
+
`defineRichTextProfile({ name, version, blocks, headings, marks, styles, components, contactPoints, contactLiterals })` narrows what a `Schema.RichText({ profile })` field holds. A save refuses anything outside the profile with the rule `profile`, and the stored document records `profile: '<name>@<version>'`. Without a profile a field behaves as before.
|
|
876
|
+
|
|
877
|
+
- Named styles from one design-token group on paragraphs, headings, lists and runs of text (`style`, markup `{name|text}`), rendered as `data-style`. - Components between the blocks (`type: 'component'`), checked, stamped and migrated like placed ones, translated through XLIFF, and edited with the same props form. - Contacts in text (`type: 'contact'`): a published contact point recorded as a reference edge, or a one-off e-mail address or phone number checked on save (`contactLiteralProblem`, `contactLiteralHref`). `RichDocumentView` takes `contactHrefOf` and `renderComponent`; `richDocumentFromHtml(html, { contacts: true })` keeps `mailto:` and `tel:` links.
|
|
878
|
+
|
|
879
|
+
Track changes in rich text: `trackChanges(base, edited, { id, by, at })` turns an edited copy into suggestions inside the document — deleted and inserted runs, inserted and deleted blocks, by whole words — and names blocks that already hold someone's suggestions as conflicts. `resolveChanges(doc, decide)` accepts or rejects them one by one, `pendingChanges` lists them, and `publishViolations` refuses a document with suggestions left (rule `pendingChanges`). `RichDocumentView` marks them with `<ins>` / `<del>`; the editor shows a block with suggestions read-only with Accept and Reject per change.
|
|
880
|
+
|
|
881
|
+
Typing together in a rich text: every paragraph, heading, list and table of an open draft is a CRDT text of its markup under `<field>#<blockId>` (`richBlockTextKey`, `sharedRichBlocks`). A collaborative `saveDraft` writes each merged text into its block (`withSharedBlocks`), a draft that moved on only in those blocks is no conflict (`changedApartFromShared`), and a block added in the session is shared once saved. `blockMarkup` / `withBlockMarkup` convert a block to and from its markup. The visual text surface keeps the caret in place when someone else's edit arrives.
|
|
882
|
+
- **Selection intents for a component's prop and for a draft not saved yet** — `@voltro/cms`
|
|
883
|
+
|
|
884
|
+
A `SelectionTarget` can name a placed component's prop (`instanceId` + `prop` in a components field), so a hero's image is chosen in the media library like an image block's. Before the first save there is no document to pin: the target names the open form instead (`formId`, exactly one of it or `documentId` + `revisionId`), and `applySelection(values, intent, answer, { formId })` applies the answer only to that form. Answers for saved drafts are checked against document and revision as before.
|
|
885
|
+
|
|
886
|
+
`ContentForm` hands every widget a `target` — the field, and for a prop of a placed component its `instanceId` and `prop` (`WidgetTarget`; a nested props form gets it through `widgetTarget`) — so a picker widget can build that intent. A component placed in the editor gets its instance id at once (`cins_…`, kept by the save), so it can be aimed at before the first save.
|
|
887
|
+
- **Resource selection intents: choose in the tool that owns the resource** — `@voltro/cms`
|
|
888
|
+
|
|
889
|
+
`encodeSelectionIntent` / `decodeSelectionIntent` / `parseSelectionIntent` carry what to pick, where the choice goes (a field, an image block, a selected text for a link), on which revision and where to return, between the editor and the tool that owns the resources (a media library, a page tree). `applySelection` puts the answer there and refuses one for another intent, another document, a revision that moved on, or a block that is gone. A `RichDocumentPicker` receives `target: { field, blockId }`.
|
|
890
|
+
- **CMS: site domains verified by a DNS TXT record; a primary domain** — `@voltro/cms`
|
|
891
|
+
|
|
892
|
+
`addDomain(tx, site, host)` records a host pending, with the TXT record that proves ownership (`_voltro-verify.<host>` → `voltro-verify=<token>`); `verifyDomain(tx, id, resolveTxt)` looks it up through a resolver the caller passes; only a verified domain becomes the site's primary (`setPrimaryDomain`, `primaryDomain`); the primary is not removed. A host belongs to one site anywhere. Register `cmsSiteDomainsTable`.
|
|
893
|
+
- **CMS: templates rule regions, widths and nesting depth** — `@voltro/cms`
|
|
894
|
+
|
|
895
|
+
A `PageTemplate` may now rule other `Schema.Components` fields of its type (`regions`: `allowed`, `required`, `maxItems`), the values a component's width prop may take (`widths`, prop named by `TemplatesConfig.width`, default `width`) and how deep components nest in slots (`maxDepth`). `templateViolations` names each break at its field; `checkTemplates` refuses a region that is not another components field or names a component it does not allow.
|
|
896
|
+
- **A translation export names what it does not carry** — `@voltro/cms`
|
|
897
|
+
|
|
898
|
+
`translationLosses(fields, values)` names what an XLIFF exchange of a document's values leaves out or flattens: a block with suggested changes (its text goes out as written and the translation comes back without suggestion marks) and a component whose definition the field no longer declares (its text is not exported). Show it with the export instead of losing it silently.
|
|
899
|
+
- **CMS: metrics as versioned definitions, drillable to revision and release** — `@voltro/cms`
|
|
900
|
+
|
|
901
|
+
`defineMetric` declares a number with its formula, unit, scope, freshness, quality limits and drill targets under an id and a version; its `fingerprint` follows the meaning, and `metricVersionDrift` names a definition whose meaning changed under an unchanged version. `revisionTimeline` reads when each revision of a document was live (and which release took it live) from the change feed, and `attributeToRevisions` gives per-day values to the revision live at the end of each day. The pure parts are also in `@voltro/cms/web`.
|
|
902
|
+
- **Comments: thread kinds, status policy, visibility, retention and status history** — `@voltro/plugin-comments`
|
|
903
|
+
|
|
904
|
+
A thread now has a `kind` — `discussion` (default), `note` or `review` — set on `comments.create`. Every status change (opening, resolve, reopen, with actor, time and an optional `reason`) is appended to the new `_voltro_comment_thread_events` table and returned as the thread's `history`; resolving tidies the open view and removes nothing. `statusPolicy` decides who may resolve or reopen which thread (`CommentStatusRefused` otherwise), and `visibleTo` hides threads — editorial notes for the team only — from `comments.list`, replies and status changes. `applyCommentRetention` removes the comments of threads resolved longer than their kind's retention, keeping the thread and recording the removal in its history. `useComments().resolve` takes a reason and `create` a kind.
|
|
905
|
+
- **An isr page can render per variant, and send a request past the shared cache** — `@voltro/cli`
|
|
906
|
+
|
|
907
|
+
A page with `renderMode: 'isr'` may export `cacheVary: { cookies, key }`. `cookies` names the cookies its shared render may see (beside `voltro:locale`; nothing else of the jar does); `key(cookies, headers)` maps a request to a variant, which joins the cache key (`|variant=<v>`), or answers `null` — then that request is rendered for itself as `ssr`, with its own credentials, and never cached. A consent-dependent page so stays cached per consent class instead of uncached for everyone, and a signed-in reader never gets nor fills a shared copy. Path revalidation drops every variant of the path. `voltro dev` applies the same rule.
|
|
908
|
+
- **Built-in durable outboxes store subject partitions atomically** — `@voltro/local-first`
|
|
909
|
+
|
|
910
|
+
Persistence adapters can implement the optional paired `loadQueuePartition` and `saveQueuePartition` methods. Built-in memory and IndexedDB adapters replace only the requested subject's queue without reading or rewriting foreign payloads. IndexedDB migrates existing shared queues atomically and resolves saves only after transaction commit. Whole-queue methods retain their order and replacement semantics. Custom adapters without both methods keep the serialized read/merge/write fallback; queue snapshots within the selected partition remain linear.
|
|
911
|
+
- **Static pages read the api at build time when its origin is set** — `@voltro/cli`
|
|
912
|
+
|
|
913
|
+
A prerendered (`renderMode: 'static'`) page's loaders now receive `query` during `voltro build` when the build can reach the api — `VOLTRO_API_ORIGIN[_<NAME>]`, the api's `serverUrl`, or an external api's absolute url — over the same one-shot HTTP rpc a server render uses, without a cookie. Without an origin `query` stays undefined, as before; a build never dials the dev proxy port.
|
|
914
|
+
- **Storage: editImage — creative edits that make a new version** — `@voltro/plugin-storage`
|
|
915
|
+
|
|
916
|
+
`editImage(bytes, contentType, { rotate, flip, flop, grayscale, crop })` returns new image bytes in the input's format — a creative edit to store as a new version, unlike the delivery transforms (`?w=&h=…`). Null when the input is no image, `sharp` is not installed, or the edit is empty.
|
|
917
|
+
- **Storage: bound how long a cached public object outlives turning private** — `@voltro/plugin-storage`
|
|
918
|
+
|
|
919
|
+
`storagePlugin({ publicCacheSeconds })` sets the `cache-control` of public objects the serve route delivers itself, transforms included (`public, max-age=<n>`). Without it they keep the year-long `immutable` cache. An object that turns private later stays reachable from a cached copy until that time runs out; a shorter time bounds the window.
|
|
920
|
+
- **A deferred webhook emit records its outbox row id as the event id** — `@voltro/plugin-webhooks`
|
|
921
|
+
|
|
922
|
+
`emit(event, payload, { eventId })` sets the event's id. An emit deferred to after commit uses its outbox row's id, so every delivery of that intent — a resent one included — records the same `eventId` (`x-voltro-event-id`), and a trace links the request (`traceId`), the outbox row and its deliveries.
|
|
923
|
+
|
|
924
|
+
### Changed
|
|
925
|
+
|
|
926
|
+
- **Aggregates and memory JOINs prepare their work once per evaluation** — `@voltro/runtime`
|
|
927
|
+
|
|
928
|
+
Materialize aggregate groups directly and prepare memory JOIN qualification, null rows and supported scalar ON membership within each evaluation.
|
|
929
|
+
- **Prepare wide array predicates and migration diagnostics** — `@voltro/database`
|
|
930
|
+
|
|
931
|
+
Prepare wide ordinary in-memory array predicates and drifted migration diagnostics locally while preserving native membership, current files and ledger identity.
|
|
932
|
+
- **Reduce fuzzy matching and repeated feed date parsing** — `@voltro/plugin-search`, `@voltro/content`, `@voltro/database`
|
|
933
|
+
|
|
934
|
+
Memory fuzzy matching computes only the allowed edit-distance band and reuses row buffers, preserving UTF-16 and threshold behavior. Feeds parse dates once before ordering while preserving stable invalid-date behavior and XML output. Charset conversion planning indexes snapshot-local columns and incident foreign keys without changing migration decisions or constraint order.
|
|
935
|
+
- **Bound pending log sends and finish shutdown after a deadline** — `@voltro/logger`, `@voltro/plugin-logship`, `@voltro/plugin-datadog`
|
|
936
|
+
|
|
937
|
+
Buffered sinks allow one active send, share flushes and bound pending records when explicit count/byte caps are configured. Sends receive an AbortSignal with a five-second default; disposal has its own five-second budget and discards unsent work at the deadline. A sender ignoring abort blocks subsequent sends instead of overlapping them. Logship serializes redacted records once; Datadog does not forward its own transport warnings.
|
|
938
|
+
- **Bound plugin reads, image work and broadcast publication** — `@voltro/plugin-storage`, `@voltro/plugin-governance`, `@voltro/plugin-audit`, `@voltro/plugin-broadcast`, `@voltro/integration-http`, `@voltro/ui`
|
|
939
|
+
|
|
940
|
+
Storage pages original references in the database, aggregates counts and usage, coalesces concurrent image transforms, and reuses authorized warm variants without reading the original body. Output caches are limited to 200 variants and 64 MiB; distinct in-flight variants are limited to 32 and saturation serves originals. Governance traverses bounded subject pages without losing descendants of capped parents, uses stable retention cursors and bounded bulk physical deletes or anonymization. Audit verification retains full history coverage while paging bodies and preserving hash-chain state, with per-writer append horizons.
|
|
941
|
+
|
|
942
|
+
Broadcast publication uses an ordered queue bounded by messages and bytes. Loss triggers an explicit peer resync even on quiet tables; shutdown drains admitted work within a deadline and reports unsent changes. Memory providers release owned subscriptions and empty channels. HTTP retry budgets maintain incremental counters, and AutoForm indexes current field descriptors once for named controls.
|
|
943
|
+
- **Reduce build, content and locale-resolution allocations** — `@voltro/cli`, `@voltro/content`, `@voltro/i18n`
|
|
944
|
+
|
|
945
|
+
Reduce temporary collection allocations in environment summaries, mobile binding generation, shell build reports, binary Inspect metadata, content loading and invalidation, and catalog parity. Reuse compiled locale and time-zone cookie patterns while retaining resolution, authorization, diagnostics and output contracts.
|
|
946
|
+
- **Production server bundles drop chunks nothing can load** — `@voltro/cli`
|
|
947
|
+
|
|
948
|
+
`voltro build` removes the `.js` chunks of the web start bundle and the api serve bundle that no file of the bundle references. esbuild emits a chunk for every `import()` target before tree-shaking and keeps it when the code holding the import is dropped; a dependency that stores a lazy loader in a module-level constant shipped a 0.5–0.8 MB chunk nobody loads. Measured on the reference fixtures: start bundle 2.1 MB → 1.0 MB (45 → 13 files), serve bundle 9.3 MB → 8.5 MB.
|
|
949
|
+
- **Inspect nested ICU placeholders with less repeated work** — `@voltro/i18n`
|
|
950
|
+
|
|
951
|
+
Prepare repeated nested ICU brace scans and long catalog messages locally while preserving placeholder order, quoting and live catalog reads.
|
|
952
|
+
- **Release paired CDC expiry timers immediately** — `@voltro/sql-mysql`
|
|
953
|
+
|
|
954
|
+
The MySQL/MariaDB inline/binlog delivery gate cancels a fingerprint's timer as soon as its echo pairs. Expiry callbacks are generation-bound, so an old callback cannot remove a newer identical write. Unpaired events retain their expiry and first-arrival delivery behavior.
|
|
955
|
+
- **Faster schema-derived controls and repeated snapshot lookups** — `@voltro/client`
|
|
956
|
+
|
|
957
|
+
Schema-derived forms collect nested fields directly, retain natural declaration order without sorting and avoid temporary filter and dropped-key lists. Repeated connection lookups prepare an ID index only after multiple reads, while long undo/redo searches reuse selections for immutable subscription snapshots. First-match behavior, pending state, explicit field order and unusual metadata fallbacks remain intact.
|
|
958
|
+
- **Delivery checks on the pages that include a document** — `@voltro/cms`
|
|
959
|
+
|
|
960
|
+
A `ReadRevision` may answer every revision an address shows — the page's own first, then the header and shared blocks it includes — as an array. A check of an included document is observed there once its revision is among them, so a header's publish can be checked on every page that shows it. `releaseCheckpoint` lists each entry's addresses, one per address checked, newest check each. A function answering a single revision as a string works as before.
|
|
961
|
+
- **A link's picker can hand the choosing to the page tree alone** — `@voltro/cms`
|
|
962
|
+
|
|
963
|
+
The rich-text editor's link control is a named group ("Link to"): a `RichDocumentPicker` gets `labelledBy`, the id that names it, to label a list it renders (`aria-labelledby`), or renders just a button that hands the choosing to the page tree with a selection intent. The link's text and "Insert link" appear once a target is chosen in the editor; a target chosen in the tree lands over the selected text by itself, so an editor without a list of pages shows no dead controls.
|
|
964
|
+
- **A draft save that changes nothing makes no revision** — `@voltro/cms`
|
|
965
|
+
|
|
966
|
+
`saveDraft` returns the draft as it is when the saved values equal it field for field: no revision, no status change, no approval to repeat. An autosave of a form that only took up what others typed together and saved used to mint a revision with the same content, which a review workflow then asked to approve again. A save with `basedOn` (a translation catching up) still writes one.
|
|
967
|
+
- **Cold SSR compile queues drain without repeated tail movement** — `@voltro/cli`
|
|
968
|
+
|
|
969
|
+
Cold SSR compilation uses the existing Effect FIFO queue for waiting permits, retaining concurrency limits, deduplication, failure recovery and immediate permit handoff while reducing queue bookkeeping during large route sweeps.
|
|
970
|
+
- **Share resolved subscription reads within committed bulk buffers** — `database`, `runtime`, `sql-postgres`, `sql-mysql`, `sql-sqlite`, `sql-mssql`
|
|
971
|
+
|
|
972
|
+
Committed local buffers share identical resolved subscription reads while preserving every row event, guard and row-filter evaluation. A newer change ends sharing for the older buffer; ordinary and ungrouped CDC changes keep their per-event reads.
|
|
973
|
+
- **Reduce repeated work in core data and observability paths** — `@voltro/protocol`, `@voltro/database`, `@voltro/runtime`, `@voltro/cli`, `@voltro/sql-postgres`, `@voltro/sql-mysql`
|
|
974
|
+
|
|
975
|
+
Row patches avoid full JSON serialization for ordinary scalar and byte rows. Memory queries prepare primitive membership and supported equality joins; range routing uses ordered thresholds. Stateful aggregates update touched groups and persist revision-fenced keyed patches atomically. Aggregate boot batches consistency-checked reads. CDC attribution, event resume counting, trace rings and local timeline replay avoid repeated scans or array shifts while retaining bounds and ordering.
|
|
976
|
+
|
|
977
|
+
CDC echoes also preserve procedure and agent attribution, including identity with no trace or subject.
|
|
978
|
+
- **Large row deliveries share repeated relation implication work** — `@voltro/runtime`
|
|
979
|
+
|
|
980
|
+
Ordinary relationship policies reuse implication decisions within one synchronous row delivery. Tenant checks, cyclic graph handling and immediate tuple or policy revocation remain intact; accessor-backed policies and tenant checks keep live evaluation.
|
|
981
|
+
- **Reduce repeated CSS graph and SPA layout work** — `@voltro/cli`, `@voltro/web`
|
|
982
|
+
|
|
983
|
+
Development SSR visits shared CSS dependencies once and deduplicates URLs without linear list searches. API discovery, closure collection and route metadata avoid intermediate lists. Prerendered SPA shells reuse the layout modules that passed the unchanged loader safety gate; request-dependent shells remain excluded.
|
|
984
|
+
- **Snapshots, boot diagnostics and log sinks build fewer temporary collections** — `@voltro/runtime`, `@voltro/logger`
|
|
985
|
+
|
|
986
|
+
Reduce intermediate collections in graph, fleet, experiment and cost snapshots, boot diagnostics, pretty logs and buffered-sink policies and drains while preserving snapshot ownership, output and sender ordering.
|
|
987
|
+
- **Reduce validation work for broad middleware route lists** — `@voltro/cli`
|
|
988
|
+
|
|
989
|
+
Startup validation prepares ordinary exact route lists once while preserving overlap witnesses, subtree exclusions and asset scope.
|
|
990
|
+
- **Prepare large form error membership and stable accessibility prefixes** — `@voltro/client`
|
|
991
|
+
|
|
992
|
+
Prepare large form error membership and stable accessibility prefixes; bound rich-text cardinality work and avoid fenced-code tail copies.
|
|
993
|
+
- **Reduce frontend and code-generation collections** — `@voltro/client`, `@voltro/web`, `@voltro/cli`
|
|
994
|
+
|
|
995
|
+
Reduce transient frontend and code-generation collections while preserving transport observations, 404 loader behavior, image candidate order and generated declaration contracts.
|
|
996
|
+
- **Client, web and CLI hot paths build fewer temporary collections** — `@voltro/client`, `@voltro/web`, `@voltro/cli`, `@voltro/local-first`
|
|
997
|
+
|
|
998
|
+
Reduce temporary collections in bounded diagnostic restoration, schema field ordering, single-scope UI checks, input restriction, dotted RPC lookup, route ordering, scoped not-found selection, memory prefix reads, scalar conflict inputs and content migration selection while preserving captured input, callback order and current state.
|
|
999
|
+
- **Indexed queues and local queries avoid whole-cache work** — `@voltro/client`, `@voltro/local-first`, `@voltro/web`
|
|
1000
|
+
|
|
1001
|
+
Reuse prepared Router priorities and target client table/tag/mutation cache memberships directly. Keep unrelated local-row memos and select stable bounded local results without sorting the entire replica.
|
|
1002
|
+
|
|
1003
|
+
Use persistent reducer pending storage and indexed SyncClient/outbox controller queues while preserving immutable public arrays, FIFO, retries, conflicts and sealed epochs. Join active SyncClient drains in flush and stop queued sends after close. Bulk-read mirror partitions in one IndexedDB transaction, with the original custom-KV fallback and oldest-first budget eviction.
|
|
1004
|
+
- **A page that renders per variant tells a cache in front what it may keep** — `@voltro/cli`
|
|
1005
|
+
|
|
1006
|
+
A `cacheVary` page's answers now name what they vary on (`Vary: Cookie` and the new optional `cacheVary.headers`, the request headers `key` reads), so a reverse proxy or CDN that honours `Vary` keeps one copy per variant. An answer for a request carrying one of `cacheVary.cookies` is `private, max-age=0, must-revalidate`: a CDN that ignores `Vary` would otherwise hand a page rendered with one reader's consent to readers who gave none — the server still keeps it per variant. A request rendered for itself (`key` → `null`) is `private, no-store`. `voltro dev` sends the same headers.
|
|
1007
|
+
- **Reduce workflow journal scanning and gateway queue movement** — `@voltro/workflow`, `@voltro/runtime`
|
|
1008
|
+
|
|
1009
|
+
Workflow run observation selects the latest terminal reply directly and update polling collects terminal update IDs without intermediate arrays. Raw WebSocket gateways drain large outbound bursts using a cursor with occasional compaction, immediately clear consumed payload references and retain FIFO order, active-plus-queued limits, cancellation and transport flush semantics.
|
|
1010
|
+
- **Reduce replay retention and eager projection planning work** — `@voltro/runtime`, `@voltro/client`, `@voltro/database`
|
|
1011
|
+
|
|
1012
|
+
Delta-resume overwrites bounded history slots without shifting retained events and locates the missed suffix by revision. Junction projections resolve column membership once per compilation, preserving requested order and missing keys. Client cache groups reuse ordered membership snapshots until membership changes; destruction and eviction release subscription-owned row-patch indexes.
|
|
1013
|
+
- **Local queries build their string sort specification once** — `@voltro/client`
|
|
1014
|
+
|
|
1015
|
+
Avoid rebuilding string-column sort specifications per local-query comparison, preserving stable and custom ordering.
|
|
1016
|
+
- **Share source preparation across localized content views** — `@voltro/content`
|
|
1017
|
+
|
|
1018
|
+
Concurrent server locale views share parsed, validated and rendered source entries within the collection cache generation. Invalidation, a changed definition, root or asset resolver starts fresh preparation. Locale fallback, default-tree validation, file-relative images and the browser artifact boundary remain intact.
|
|
1019
|
+
- **Reduce intermediate allocation in memory reads and runtime diagnostics** — `@voltro/runtime`, `@voltro/cli`
|
|
1020
|
+
|
|
1021
|
+
Memory reads without predicates avoid a redundant result copy, and small full-text prefix searches count matches directly. Event replay, route snapshots, preview summaries, payload diagnostics, metric projections and level-reaction state reads avoid intermediate lists while retaining their existing ordering, gap, validation and recovery semantics.
|
|
1022
|
+
- **Reduce nullable field and pending outbox allocation** — `@voltro/client`
|
|
1023
|
+
|
|
1024
|
+
Nullable fields with a single non-null schema branch avoid temporary branch lists, and pending outbox counts fold the queue directly. Field metadata, enum options, pending/failed status counting and conflict replay boundaries remain unchanged.
|
|
1025
|
+
- **External ID metadata also describes local polymorphic targets** — `@voltro/database`
|
|
1026
|
+
|
|
1027
|
+
Clarify that `.externalId()` means an identifier has no single database foreign key target, including polymorphic references to rows in the same database. Storage stays unchanged; the application validates targets and handles orphans.
|
|
1028
|
+
- **Form length bounds and capability scopes are prepared once per check** — `@voltro/client`
|
|
1029
|
+
|
|
1030
|
+
Prepare wide form length-bound metadata per validation and large declared capability scope membership per decision, retaining nested/localized errors and unknown resource authority.
|
|
1031
|
+
- **Prepare repeated metadata once per operation** — `@voltro/client`, `@voltro/database`, `@voltro/runtime`, `@voltro/api-client`, `@voltro/ai`, `@voltro/mcp`, `@voltro/plugin-notifications`, `@voltro/plugin-presence`, `@voltro/data-transfer`, `@voltro/cache`, `@voltro/kv`, `@voltro/plan`, `@voltro/plugin-flags`, `@voltro/plugin-row-history`, `@voltro/plugin-webhooks`, `@voltro/cli`, `@voltro/devtools-ui`
|
|
1032
|
+
|
|
1033
|
+
Transient agent hooks batch immutable publication and incrementally build text. Bulk encrypted writes prepare metadata once while retaining per-row authentication binding; generated wire codecs prepare field lookups per call. Admin/form lookups, policy glob compilation, manifest refreshes, subset closure, flag allocation and impact traversal avoid repeated work. Notifications use aggregate unread counts and bulk mark-all updates. Empty presence branches and expired KV entries are reclaimed. Semantic lookup extracts nearest candidates without sorting all of them. Webhook admissions use bounded concurrency; failed admission does not roll back earlier targets. Descriptor mutation and live authorization remain visible.
|
|
1034
|
+
|
|
1035
|
+
Workflow statistics tally a page once and skip page-duration sorting when server percentiles are available. Memory API-key hash lookup scans live rows without copying the full map; it does not cache authentication. Data transfers retain inspect-create receipts in their source deployment and carry comment-thread status history with the comments.
|
|
1036
|
+
- **Prepare membership, authorization and migration matching per operation** — `@voltro/plugin-scim`, `@voltro/protocol`, `@voltro/database`, `@voltro/plan`
|
|
1037
|
+
|
|
1038
|
+
Large SCIM group patches, repeated global guards, index rename matching and executable plan evaluation avoid repeated scans and growing array copies. Ordering, ambiguity rules, evidence validity and fresh authorization remain unchanged; small lookups retain direct short-circuit paths.
|
|
1039
|
+
- **Prepare broad public API routes once** — `@voltro/protocol`
|
|
1040
|
+
|
|
1041
|
+
Mounted public API routes prepare query and path coercion metadata and index error-response members at construction, preserving validation, response order and plugin error status behavior. Large REST ANY-scope guards prepare membership only for the current call so later revocation remains visible.
|
|
1042
|
+
- **Prepare release and database reads without repeated scans** — `@voltro/plugin-releases`, `@voltro/database`, `@voltro/runtime`
|
|
1043
|
+
|
|
1044
|
+
Batch release artefact summaries and prepare overview memberships per call. Resolve joined result decoder metadata once per result and load nested portable eager relations only for each parent's visible many window.
|
|
1045
|
+
|
|
1046
|
+
Retain memory transaction snapshots through copy-on-write row maps instead of copying all rows at transaction opening. Reuse immutable JSON row length estimates for blocked subscription accounting; custom/non-JSON values keep native estimates and existing backpressure policies.
|
|
1047
|
+
- **Row decoding and flat-row comparison avoid redundant cell work** — `@voltro/database`, `@voltro/protocol`
|
|
1048
|
+
|
|
1049
|
+
Table-row decoders prepare only columns whose dialect representation needs normalization, while preserving fresh shallow copies and unknown columns. Flat rows with matching field layouts compare guarded property values without per-row Maps; exotic values and differing layouts retain native JSON behavior.
|
|
1050
|
+
- **SQLite named composite UNIQUE no longer re-issues unchanged DDL** — `@voltro/database`
|
|
1051
|
+
|
|
1052
|
+
Recover unambiguous named SQLite composite UNIQUE identity to avoid repeated unchanged-schema DDL; prepare file-migration probe membership, physical decimal source metadata, large mixin index composition without retaining mutable decisions between calls.
|
|
1053
|
+
|
|
1054
|
+
Preserve same-name composite UNIQUE constraints during verified column renames without redundant drop/add work.
|
|
1055
|
+
- **Runtime snapshots copy less; consumed broadcast payloads are released** — `@voltro/runtime`, `@voltro/local-first`, `@voltro/plugin-broadcast`
|
|
1056
|
+
|
|
1057
|
+
Reduce temporary runtime snapshot collections and release consumed broadcast payload references while preserving ordered delivery, replay, connection teardown and offline lineage behavior.
|
|
1058
|
+
- **Reduce reactive form and loader bookkeeping allocations** — `@voltro/client`, `@voltro/runtime`, `@voltro/web`
|
|
1059
|
+
|
|
1060
|
+
Form errors, numeric native POST values, patch memberships and loader keys avoid temporary lists. Query pickers keep default inputs stable across unchanged renders, and single aggregate-group reads avoid a deduplication Set. Client diagnostics restore large persisted histories without exceeding JavaScript argument limits. Validation, reconciliation, group order, SSR handoff and loader cancellation retain their existing contracts.
|
|
1061
|
+
- **Reduce reactive fan-out and workflow inspection overhead** — `@voltro/runtime`, `@voltro/workflow`, `@voltro/local-first`
|
|
1062
|
+
|
|
1063
|
+
Reactive deliveries share ordinary stripped snapshots within the current column-visibility policy while retaining per-subscriber authorization. Programmatic workflow inspection indexes first-step rows when ordering large histories, and offline mirrors prepare wide encrypted-column membership without retaining metadata across saves.
|
|
1064
|
+
- **Subscription routing scales with active index definitions** — `@voltro/runtime`
|
|
1065
|
+
|
|
1066
|
+
Composite subscription routing looks up the event's tuple per active index definition instead of scanning every tuple bucket. Subscription removal updates only its own index memberships and discards empty buckets. Resume retention uses bounded incremental cleanup while checking each requested ring's expiration directly, preserving replay admission, authorization and row-filter behavior.
|
|
1067
|
+
- **Combine replica traces with linear metadata scans** — `@voltro/protocol`
|
|
1068
|
+
|
|
1069
|
+
Select earliest replica traces and accumulate trace metadata with bounded linear passes while retaining span ordering and partial observation evidence.
|
|
1070
|
+
- **Share running request-loader reads** — `@voltro/runtime`
|
|
1071
|
+
|
|
1072
|
+
Request-scoped loaders share overlapping in-flight reads while preserving batching, positional results, isolation and failure retries.
|
|
1073
|
+
- **A cached REST answer can decide who may share it; failures are never shared** — `@voltro/protocol`
|
|
1074
|
+
|
|
1075
|
+
`cache.audience(output, input, subject)` lets the answer decide instead of the request's cookies: `'public'` — the same for every reader, shared at the CDN even when the request carries cookies such as a consent choice (`Vary: Authorization` only); `'private'` — this reader's alone, `private, no-store`. Answer `'private'` for a signed-in subject whenever the answer depends on who reads it. Without `audience`, any cookie still makes an answer private, and `Authorization` always does.
|
|
1076
|
+
|
|
1077
|
+
Any answer other than a `200` or `304` of a GET route with `cache` — a `404`, `410`, `401` — is now `private, no-store`, so a reader's "not found" (for example a members page an anonymous reader may not see) never lands in a shared cache. Before, failures carried no cache headers and a CDN rule could keep them.
|
|
1078
|
+
- **Reduce captured runtime and form collection overhead** — `@voltro/runtime`, `@voltro/client`
|
|
1079
|
+
|
|
1080
|
+
Reduce transient collections during memory full-text matching, subscription registration, tenant listings, proxy parsing, connection scope projection, mirror high-water reads, RPC capability publication and asynchronous form submission. Preserve snapshot capture, validation, staged reads and trust boundaries.
|
|
1081
|
+
- **Runtime diagnostics, mounts and savepoints build fewer temporary collections** — `@voltro/runtime`
|
|
1082
|
+
|
|
1083
|
+
Reduce temporary collections in replica, membership and expectation diagnostics, HTTP mount planning, source-gap reports, resume input normalization, ranked memory reads, savepoint capture and commit table bookkeeping while retaining ownership, ordering and transaction semantics.
|
|
1084
|
+
- **Reduce routing and burst bookkeeping overhead** — `@voltro/runtime`
|
|
1085
|
+
|
|
1086
|
+
Replica recovery removes expired failures in one pass. Subscription error queues keep ordered delivery without repeatedly shifting their backlog. Analytics mirrors prepare table routing, HTTP mounts resolve wildcard ancestors by segment boundaries, malformed workflow payload diagnostics prepare provided-name membership once, and memory joins reuse alias/projection metadata.
|
|
1087
|
+
- **Raw reads, updates and in-memory full-text prefixes are prepared once** — `@voltro/runtime`
|
|
1088
|
+
|
|
1089
|
+
Prepare wide immutable raw-read identity, explicit update membership and large in-memory full-text prefixes without retaining mutable decisions or changing authority.
|
|
1090
|
+
- **Cheaper cleanup for shared pending scripts** — `@voltro/web`
|
|
1091
|
+
|
|
1092
|
+
Unmounting a pending Script consumer removes its callback by identity without searching the other listeners. Process-wide script deduplication, callback order, settled remount callbacks and StrictMode cleanup keep their existing behavior.
|
|
1093
|
+
- **Reuse Intl formatters and batch comment lists** — `@voltro/i18n`, `@voltro/cache`, `@voltro/plugin-comments`, `@voltro/cli`
|
|
1094
|
+
|
|
1095
|
+
Reuse Intl instances for equivalent ordinary locale/options combinations with bounded LRU caches, preserving explicit date zones, fallback and render clocks. The memory cache maintains FIFO links atomically with values and tags instead of sorting all entries at capacity. Comment lists batch visible-thread children and reactions within portable parameter limits, retaining access, tenant, unread and ownership checks. Value-import traversal enqueues each source file once without changing browser-safety reach or per-walk invalidation.
|
|
1096
|
+
- **Snapshot restore and foreign-key metadata are read once per boundary** — `@voltro/database`
|
|
1097
|
+
|
|
1098
|
+
Prepare large snapshot restoration and ownership lookups, deduplicate identifier warnings, avoid composite foreign-key prefix copies and repeated parent metadata reads, and reserve exact CTE helper names once per boundary.
|
|
1099
|
+
- **Reduce repeated work while scanning source trees** — `@voltro/cli`
|
|
1100
|
+
|
|
1101
|
+
Avoid recursive source-file result copies and repeated ordinary exclusion-list validation while preserving mutable filters, traversal order and vanished-directory handling.
|
|
1102
|
+
- **Reduce subscription metadata and idle event-route selection work** — `@voltro/client`, `@voltro/runtime`
|
|
1103
|
+
|
|
1104
|
+
Stable object keys and confirmed subscription patch IDs avoid intermediate lists. Event route pressure selects a bounded cold idle batch, retaining equal-time ordering, live subscribers and the fallback for unusual clocks. Key semantics, reconciliation and delivery guarantees remain unchanged.
|
|
1105
|
+
- **Bound upload progress and snapshot dependency planning costs** — `@voltro/client`, `@voltro/i18n`, `@voltro/cli`
|
|
1106
|
+
|
|
1107
|
+
Batch upload progress updates a sum tree instead of rescanning every file, retaining cancellation, retries and current file-list reads. Snapshot cleanup prepares dependency adjacency once and uses an iterative child-first walk, preserving foreign-key ownership checks and ordering without recursive stack exhaustion. Store keys, reconnect cursors, native form paths, deep links, rich-text bodies and locale negotiation avoid unnecessary intermediate collections. Public APIs stay unchanged; upload fractions may differ in their last floating-point bits.
|
|
1108
|
+
- **Reduce repeated searches in broad migration plans** — `@voltro/database`
|
|
1109
|
+
|
|
1110
|
+
Migration planning prepares enum and renamed-column lookups locally. SQLite introspection indexes virtual-table artifacts, and partial unique-index preflight prepares changed table shapes without skipping duplicate probes or convergence checks.
|
|
1111
|
+
- **Wide SQL projections prepare repeated compiler lookups** — `@voltro/database`
|
|
1112
|
+
|
|
1113
|
+
JOIN alias routing, projection ordering, grouped aggregate inputs, exact decimal windows and rows, parallel decoder validation and MSSQL decimal DISTINCT reuse compilation-local lookup metadata. Generated SQL, binding order, exact values, NULL semantics and numeric ordering are preserved; registry column definitions stay live.
|
|
1114
|
+
- **Reduce transient wire and route preparation collections** — `@voltro/protocol`
|
|
1115
|
+
|
|
1116
|
+
Reduce temporary wire collections during event addressing, replica inspection, REST decoding and validators, public route preparation and plugin alias/compatibility resolution. Preserve validation, capture order, error messages and conditional caching.
|
|
1117
|
+
- **Reduce repeated schema work in writes, privacy graphs and field visibility** — `@voltro/database`, `@voltro/runtime`
|
|
1118
|
+
|
|
1119
|
+
Conflict-key validation reads only named column metadata. Larger ordinary bulk writes prepare required column names once per batch, privacy graph derivations prepare reverse-reference indexes per walk, and field visibility indexes larger ordinary scope sets per synchronous decision. Rights and schema metadata remain fresh between calls; custom validators and exotic containers retain live checks. Foreign-key dependency ranking no longer copies growing ancestor sets, preserving parent-first migration order and deferred cycle handling.
|
|
1120
|
+
|
|
1121
|
+
### Fixed
|
|
1122
|
+
|
|
1123
|
+
- **voltro build resolves an unset NODE_ENV to production** — `@voltro/cli`
|
|
1124
|
+
|
|
1125
|
+
`voltro build` now treats an unset `NODE_ENV` as `production`, as `serve`, `start`, `db` and `migrate` already do. Left unset, the prerender's Vite server declared it `development`, React loaded its development build, and a static page failed to render against the production `react-dom` with `dispatcher.getOwner is not a function`. A declared `NODE_ENV` still wins.
|
|
1126
|
+
- **Reuse client lookup metadata and bound URL-prefix checks** — `@voltro/client`
|
|
1127
|
+
|
|
1128
|
+
Client scheduler callback queues drain without shifting their backlog. Derived values reuse stable source-key ordering and correctly invalidate when a source name changes. Permission providers share scope membership for larger ordinary arrays and rebuild it on every delivery. Rich-text URL checks inspect only the normalized allowlisted prefix while preserving the existing validation rules.
|
|
1129
|
+
- **A component in a rich text records its reference props as edges** — `@voltro/cms`
|
|
1130
|
+
|
|
1131
|
+
`referencesOf` walks the props of a component placed between the blocks of a rich text, as it does for a placed component: a call to action in the body pointing at a page is a reference edge, so `usedBy`, publishing checks and the dependency graph see it.
|
|
1132
|
+
- **CMS: a field typed before its shared text arrived is no longer lost on save** — `@voltro/cms`, `@voltro/local-first`
|
|
1133
|
+
|
|
1134
|
+
A collaborative `saveDraft` took every shared field from the merged text, so a value changed in the form before that text reached the editor was replaced by the revision's old value. Such a value is now saved — while nobody typed into the text or saved the field since the revision the editor saw — and written into the shared text as one edit. `@voltro/local-first` exports `crdtTextEdit` and `rebaseEdit` from its main entry for server code.
|
|
1135
|
+
- **CMS: a unique field's duplicate is refused at its field, not by the database** — `@voltro/cms`
|
|
1136
|
+
|
|
1137
|
+
`saveDraft` and `publish` now refuse a value of a `Schema.unique()` field that another entry of the same tenant and scope already holds, as `ContentValidationFailed` with the rule `unique` at that field, before writing. Before, the write reached the database constraint: callers got an untyped constraint error, and in postgres the failed statement aborted the transaction, so a mutation could not even answer with its own result. The constraint stays as the backstop for a race.
|
|
1138
|
+
- **Content collections resolve their lazy server entry in native Node** — `@voltro/content`
|
|
1139
|
+
|
|
1140
|
+
The lazy content render import includes its emitted `.js` extension. Built collections now resolve in native Node without a TypeScript loader while the render pipeline remains outside the browser chunk graph.
|
|
1141
|
+
- **CRDT history uses a short framework-owned index name** — `@voltro/runtime`
|
|
1142
|
+
|
|
1143
|
+
The CRDT history index uses an explicit short name so SQL apps no longer receive an identifier-length warning for a table they do not own. Schema migration renames the existing index (replaces only the index on SQLite/Turso) and preserves archived history.
|
|
1144
|
+
- **db branch: a plain and a partial index on the same columns keep their own names** — `@voltro/database`, `@voltro/cli`
|
|
1145
|
+
|
|
1146
|
+
`voltro db branch` restores the parent's index names on the branch by matching each copied index by its key columns and uniqueness. Two indexes on the same columns — one plain, one partial (`WHERE "readAt" IS NULL`) — could swap names, and a rehearsal of an already converged schema then reported `infidelity`. The match now includes the partial index's predicate (`BranchIndexName.where`).
|
|
1147
|
+
- **Every @effect/* package the framework uses is pinned exactly** — `@voltro/runtime`, `@voltro/cli`
|
|
1148
|
+
|
|
1149
|
+
`@effect/opentelemetry` was the one `@effect/*` peer declared as `^4.0.1`. A fresh install resolved it to 4.0.2, which requires an `effect` newer than the 4.0.1 the framework pins and imports `effect/Version` that 4.0.1 does not have — `voltro build` failed in the serve bundle, and pnpm split `@voltro/runtime` into several copies. It is pinned to 4.0.1 like `effect` and the SQL drivers. `voltro update` now narrows a range that admits an exactly pinned framework peer and later versions (`^4.0.1` against `4.0.1`) to that version; a range ahead of the pin is left alone.
|
|
1150
|
+
- **The Effect 4 codemod leaves your own Left / Right unions and Type lookups alone** — `@voltro/cli`
|
|
1151
|
+
|
|
1152
|
+
The `0.85.0/90_effect-4` codemod rewrote every `x._tag === 'Left'` and every `{ _tag: 'Left', … }` in a file that imports from `effect`, including unions of your own with those tags. A comparison is now rewritten only in a file that names `Either` or `.either`, and an object literal only when it has Either's `left` / `right` shape. `X['Type']` becomes `X['Service']` unless `X` is a type alias, interface, enum or non-service class the same file declares. `HttpClientRequest.del` becomes `HttpClientRequest.delete`, also where it is passed without a call.
|
|
1153
|
+
- **Env values are shared by every copy of @voltro/env in a process** — `@voltro/env`
|
|
1154
|
+
|
|
1155
|
+
pnpm links one copy of `@voltro/env` for each set of peers its dependents resolve, so a workspace can load two copies in one process — the app's and `@voltro/testing`'s, for example. Each copy kept its own snapshot: values installed by the boot gate or by `makeTestContext({ env })` through one copy were invisible to the other, and `getSecret` failed with "read … before the boot env gate ran". The snapshot, the live-rotation overlay and the clock now belong to the process.
|
|
1156
|
+
- **The wildcard event descriptor matches guarded events again** — `@voltro/protocol`
|
|
1157
|
+
|
|
1158
|
+
A helper that infers an event's payload with `D extends EventDescriptor<string, Schema.Top, infer Payload>` resolved to `never` for every event declared with `guards:` — the guard type was derived from the key's `Type`, which is `unknown` for `Schema.Top`. The wildcard key now reads like `AnyEventDescriptor`, so the helper infers the payload of every event.
|
|
1159
|
+
- **A bundled gRPC surface serves grpc.health.v1 and reflection again** — `@voltro/cli`
|
|
1160
|
+
|
|
1161
|
+
`grpc-health-check` and `@grpc/reflection` read their own `.proto` files at runtime through `__dirname`. Inlined into the ESM serve bundle, the health import threw and was swallowed, so a production gRPC surface built with `voltro build` silently served no `grpc.health.v1`, and reflection had the same problem. Both are now runtime leaves resolved from where `@voltro/cli` is installed, like the other optional dependencies the serve bundle keeps out.
|
|
1162
|
+
- **Reduce live table rendering and release subscription buffers** — `@voltro/ui`, `@voltro/client`, `@voltro/runtime`
|
|
1163
|
+
|
|
1164
|
+
Reuse unchanged live table cells during parent updates and row reordering, while changed rows, columns and custom renderers remain reactive. Aggregate socket buffer bytes across subscriptions sharing a query label and release closed or overrun row state without losing queued patches or errors. Cancel unread capability-probe response bodies when discovery returns unavailable.
|
|
1165
|
+
- **A MySQL file migration keeps to two connections** — `@voltro/database`
|
|
1166
|
+
|
|
1167
|
+
A transactional file migration on MySQL or MariaDB checks, after its body, whether something committed the transaction from inside. That check opened a third connection while the migration lock and the transaction held theirs, so with a pool of two (`DB_MAX_CONNECTIONS=2`) `voltro db apply` waited forever with the lock held — and every later apply waited on that lock. The check now reads through the lock's own connection, which is idle while the migration runs; `HeldMigrationLock` exposes it as `connection`.
|
|
1168
|
+
- **Number fields keep their bounds and formField metadata on Effect 4** — `@voltro/protocol`, `@voltro/client`
|
|
1169
|
+
|
|
1170
|
+
Effect 4 writes a plain `Schema.Number` as a union with the `"Infinity"`, `"-Infinity"` and `"NaN"` spellings and attaches neither its bounds nor any check fragment to it. A number field therefore lost its `minimum` / `maximum` in the OpenAPI document, AI tool and WebMCP schemas, and its `formField` label, section and widget in forms and data tables — the column header fell back to the field name. JSON schemas are now generated from a finite view of every number (`finiteNumberView`, exported from `@voltro/protocol`), which is what the JSON wire carries: `type: number` with its bounds and metadata. Decoding is unchanged.
|
|
1171
|
+
- **One vitest peer range across the framework; voltro update aligns it again** — `@voltro/workflow`
|
|
1172
|
+
|
|
1173
|
+
`@voltro/workflow` declared its optional vitest peer as `^5.0.0` while `@voltro/testing` declared `>=3.0.0`. With two ranges for one peer, `voltro update` could only report a conflict and leave the app's vitest as it was. Both now declare `>=3.0.0`, and a repository check keeps every shared peer on one range.
|
|
1174
|
+
- **voltro add patches pins Effect's transitive companion to the effect version** — `@voltro/cli`
|
|
1175
|
+
|
|
1176
|
+
`@effect/platform-node` asks for `@effect/platform-node-shared` by caret, and each release of it peers on its own `effect` version, so a fresh install of a 0.85 app pulled a newer one than the exact `effect` it pins — a peer conflict, and with the next Effect release possibly an `effect` API the pinned version lacks. `voltro add patches` (and every new project) now writes a pnpm `overrides` entry that pins it to the patched `effect` version, and `voltro dev` / `voltro serve` name it when the installed one drifted.
|
|
1177
|
+
- **Preserve optimized work and lifetimes on Effect 4** — `@voltro/ai`, `@voltro/client`, `@voltro/cli`, `@voltro/runtime`, `@voltro/protocol`
|
|
1178
|
+
|
|
1179
|
+
Use Effect 4 collection and stream APIs in optimized queues and batched replay. Capture streaming readers before waking them so synchronous re-registration cannot loop within one notification; retain optional workflow payload keys and route-specific stylesheets with lazy shell loading.
|
|
1180
|
+
- **pgvector columns decode on the Effect 4 postgres driver** — `@voltro/sql-postgres`
|
|
1181
|
+
|
|
1182
|
+
`vector`, `halfvec` and `sparsevec` columns read as their text form (`[1,2,3]`, `{1:1,3:2}/5`), the shape the row decoder parses, with the same decimals pgvector prints. The driver decoded these extension types' binary values as UTF-8 and failed the query. Extension types are now resolved again after a `CREATE EXTENSION` through the client, so a migration that installs pgvector or PostGIS on a running pool does not leave those columns undecodable until a restart.
|
|
1183
|
+
- **Plugin references retain their orphan policy through column modifiers** — `@voltro/database`, `@voltro/runtime`
|
|
1184
|
+
|
|
1185
|
+
`pluginRef` keeps its target and orphan rules through immutable column modifiers, including `.nullable()` and `.renamedFrom()`. A null orphan policy now refuses a non-nullable column during table declaration, as documented. References contributed by schema mixins are collected and validated too.
|
|
1186
|
+
|
|
1187
|
+
Orphan cleanup uses the same predicate constructor as native SQL stores, so registered delete/null policies also execute against real databases.
|
|
1188
|
+
- **Concurrent first callback upserts of one key all land on postgres** — `@voltro/sql-postgres`
|
|
1189
|
+
|
|
1190
|
+
A function-form `upsert` (`update: (existing) => …`, the counter pattern) locks the existing row, so concurrent upserts of a key that exists never lose an update. When the row did not exist yet, every concurrent transaction inserted it and all but one failed with a unique violation. The INSERT now yields (`ON CONFLICT DO NOTHING`) to the one that won, locks that row and applies the patch to it: many first upserts of one key all count. MySQL/MariaDB and SQL Server still look up and write separately; the documentation says so.
|
|
1191
|
+
- **A workflow's rateLimit() counts the run once, not once per replay** — `@voltro/workflow`
|
|
1192
|
+
|
|
1193
|
+
`rateLimit()` consults the `RateLimiter` inside a journaled activity again, as it did before 0.85.0. A workflow body runs from the top after every suspension; consulting the limiter directly in the body took another token on each replay and could choose a different wait than the journal had recorded. The admission now reserves its slot once (`onExceeded: 'delay'`), records the answer, and waits for the slot with a durable sleep. The `name` identifies the admission within the run: two calls with one name share one decision, so a loop gives each call its own (`send/${i}`).
|
|
1194
|
+
- **voltro serverless build . bundles the functions in that directory** — `@voltro/cli`
|
|
1195
|
+
|
|
1196
|
+
`voltro serverless list|build|serve|deploy` took every path argument as a function file, so the documented `voltro serverless build .` (the `build` script of the edge-functions template) tried to bundle the directory and failed, and `serve .` refused to start. A directory argument now means the functions discovered in it; a file argument still names one function.
|
|
1197
|
+
- **SQLite decimal review lists executable declarations for relevant stores** — `@voltro/cli`
|
|
1198
|
+
|
|
1199
|
+
SQLite decimal upgrade guidance identifies imported schema calls, including aliases, and table SQL instead of test titles, editor labels or prose. Explicit PostgreSQL, MySQL, MariaDB and MSSQL app stores have no SQLite migration hit list. The review is reissued for upgrades from 0.84.0; unscanned dependency schemas and unresolved dynamic store configurations still require a qualified operational review.
|
|
1200
|
+
- **Retry SQLite transactions after a concurrent WAL snapshot change** — `@voltro/database`
|
|
1201
|
+
|
|
1202
|
+
SQLite `SQLITE_BUSY_SNAPSHOT` contention now retries the complete transaction with a fresh snapshot under the existing bounded retry policy. Concurrent read-then-write operations keep their atomicity without treating stale snapshots as permanent store failures.
|
|
1203
|
+
- **A page render stops presenting a session the api rejected** — `@voltro/cli`
|
|
1204
|
+
|
|
1205
|
+
A session cookie the api refuses (revoked or expired server-side, signed with a retired secret) turned every page whose loader reads into a 500 for the reader still holding it — after a secret rotation, for every signed-in reader. `voltro dev` and `voltro start` now make a refused server-side loader read once more without the framework session cookie and clear that cookie on the response; later reads in the same render go without it. The api still never reads a rejected credential as anonymous. A no-JS form POST is not retried, and a refused credential other than the session cookie is answered as before.
|
|
1206
|
+
- **Startup reuses source inventories and clears completed teardown deadlines** — `@voltro/cli`, `@voltro/runtime`
|
|
1207
|
+
|
|
1208
|
+
Web generation reuses its validated page tree, indexes layout lookup, and prepares large middleware matchers once. Import walks share contextual package resolution and metadata, while browser fingerprints reuse already-read source bytes; later walks remain fresh. Shared schema edits also invalidate the development API client. API discovery excludes reserved `.framework` output. Production reads islands shells only for on-demand renders, preserving stylesheet links and the full-shell fallback. Repeated missing-table diagnostics share suggestion work without dropping procedure warnings. Asynchronous startup teardowns cancel completed timeout timers while retaining LIFO cleanup and the five-second ceiling.
|
|
1209
|
+
|
|
1210
|
+
Compiled API production shutdown now calls the shared runner's actual shutdown handle, so registered startup cleanup also runs on graceful termination.
|
|
1211
|
+
- **A derivative is never more open than its original** — `@voltro/plugin-storage`
|
|
1212
|
+
|
|
1213
|
+
A rendition or poster (`derivedFrom`) is checked against its original: `checkAccess`, `mintUrl` and the serve route decide by the original's visibility, policy and grants, so a public derivative of a private original is no longer served to anyone, a grant or token for the original opens its derivatives, and a derivative whose original is gone answers 404. `setVisibility` on an original now takes its derivatives along — an image made private no longer leaves public renditions behind.
|
|
1214
|
+
- **Encrypted transfers release input streams and avoid repeated buffer copies** — `@voltro/data-transfer`, `@voltro/plugin-openapi`
|
|
1215
|
+
|
|
1216
|
+
Encrypted transfers process fragmented input without repeatedly copying a growing frame buffer; decryption closes its input iterator on cancellation or failure and rejects oversized frames and invalid final markers. Schema-drift comparisons index target tables once per comparison. OpenAPI prepares version groups, specs and the Swagger viewer together at surface updates, retaining the last successful surface if preparation fails and checking the Bearer gate on every request.
|
|
1217
|
+
- **An undeclared input key is refused inside class and transformed inputs too** — `@voltro/protocol`
|
|
1218
|
+
|
|
1219
|
+
Procedure inputs refuse a key the schema does not declare instead of dropping it. Under Effect 4 that rule stopped at a node carrying its own encoding: a `Schema.Class` input, a class nested in a struct, or a struct piped through `Schema.decodeTo` decoded an extra key silently. Such a node is now closed on its wire form and decoded through its own transformation, so the call fails with the key's name like every other input, and the decoded value is still the class instance.
|
|
1220
|
+
- **Summary metric snapshots use fewer temporary arrays** — `@voltro/runtime`
|
|
1221
|
+
|
|
1222
|
+
Summary metric snapshots collect populated quantiles directly without allocating a temporary array for every quantile. Zero values and quantile order are preserved, absent values are excluded, and earlier snapshots remain unchanged.
|
|
1223
|
+
- **Timeline preparation follows the recording mode** — `@voltro/cli`
|
|
1224
|
+
|
|
1225
|
+
Schema maintenance with recording disabled no longer provisions Timeline capture or its workflow-storage bootstrap. Existing capture triggers and generation helpers are retired during explicit disabled maintenance while application rows and private capture tables remain. Disabled replica startup preserves another replica's active capture contract. Explicitly enable recording on both schema maintenance and serving to prepare production history.
|
|
1226
|
+
- **Optional Timeline failures preserve successful schema maintenance** — `@voltro/cli`
|
|
1227
|
+
|
|
1228
|
+
MySQL/MariaDB and SQL Server optional capture preparation includes its workflow-storage bootstrap: an insufficient connection pool now warns instead of failing a schema command after its migrations committed. Warnings retain the underlying cause, including database driver codes, error numbers and missing privileges such as error 1419 and SUPER. Cancellation still interrupts preparation.
|
|
1229
|
+
- **Turso stores share the transaction key with every other SQL client** — `@voltro/sql-turso`
|
|
1230
|
+
|
|
1231
|
+
A statement issued through a second Turso client — a plugin's own runtime, a second layer build — inside a store transaction now runs on that transaction's connection, like every other dialect since 0.85.0. Before, it took a fresh connection and waited on the transaction's write lock until it timed out.
|
|
1232
|
+
- **voltro update aligns catalogs and writes ranges YAML can read** — `@voltro/cli`
|
|
1233
|
+
|
|
1234
|
+
`voltro update` now also moves a peer held below the framework's floor in a pnpm catalog — `catalog:` and `catalogs:` in `pnpm-workspace.yaml` — because a manifest that declares `catalog:` resolves there, not in the manifest. The YAML rewrite quotes a range YAML would otherwise read as syntax (`>=4.0.1`), keeps CRLF line endings, ends a section at a quoted top-level key, and leaves a range it cannot read as one value (`^3.0.0 || ^4.0.0`) as written.
|
|
1235
|
+
- **voltro update aligns peers installed under a workspace member and in overrides** — `@voltro/cli`
|
|
1236
|
+
|
|
1237
|
+
`voltro update` aligns the peer ranges the framework requires — `effect` among them — with what the workspace declares. It read the installed `@voltro/*` packages only from the workspace root, where pnpm links nothing unless the root itself depends on the framework, so in a pnpm workspace the alignment was skipped without a word. It now reads each member's packages, and it also moves a peer held below the framework's floor in `pnpm-workspace.yaml` `overrides`, the root `pnpm.overrides`, npm `overrides` or yarn `resolutions` — an override wins over every manifest, so moving only the manifests left the install where it was.
|
|
1238
|
+
|
|
1239
|
+
An update run by an older CLI cannot benefit from that fix, because the older CLI performs the bump. The new CLI therefore checks the installed `effect` before it loads anything else: at the codemod step of an update it aligns the workspace's peers, re-installs and continues; any other command names the version it needs and the command that repairs it, instead of failing with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
|
|
1240
|
+
- **voltro update --wait asks the registry the project installs from** — `@voltro/cli`
|
|
1241
|
+
|
|
1242
|
+
`voltro update`'s registry probe — `--wait`, and the diagnosis after a failed install — asked `registry.npmjs.org` whatever the project was configured for, so a mirror or a local registry was polled at the wrong place and reported versions missing that the install would have found. It now resolves the `@voltro` scope's registry, else the default registry, through `npm config get`, and falls back to npmjs only when neither is set. The printed endpoints name the same registry.
|
|
1243
|
+
- **voltro update runs the source transforms in every framework workspace member** — `@voltro/cli`
|
|
1244
|
+
|
|
1245
|
+
The version bump moves every workspace member that declares `@voltro/*`, but the codemods that rewrite source ran only in the app `voltro update` was started from — a web app or a shared package beside it kept the old API while its dependencies moved. The transforms now run in each of those members too, once per file; a member that does not use the framework is not touched, and the written notes still search the whole workspace once.
|
|
1246
|
+
|
|
1247
|
+
### Internal (no consumer-facing effect)
|
|
1248
|
+
|
|
1249
|
+
- **Prepare file-safe UI installation and update internals** — `@voltro/cli`
|
|
1250
|
+
|
|
1251
|
+
Prepare internal registry resolution, installed-file state and atomic file updates. The internals preserve customized and deliberately deleted files, reject stale plans and symlinked targets, and retain concurrent edits during rollback. Add additive Base UI configuration planning and AST-based alias adaptation that preserves complete source/license bytes outside actual module specifiers. This change does not register a new CLI command.
|
|
1252
|
+
|
|
1253
|
+
Prepare CSS compilation from delivered theme/style data, with light/dark token validation, Tailwind v4 utility bridges and rejection of invalid rules, references and unsafe values.
|
|
1254
|
+
|
|
1255
|
+
---
|
|
1256
|
+
|
|
42
1257
|
## [0.84.0] — 2026-10-07
|
|
43
1258
|
|
|
44
1259
|
### ⚠ BREAKING
|