@voltro/plugin-auth 0.83.1 → 0.85.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 +1191 -0
- package/THIRD-PARTY-NOTICES.md +1 -57
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/password.js +2 -2
- package/dist/{plugin-CNW-X70p.js → plugin-CbmcyEJs.js} +27 -24
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,1197 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.85.0] — 2026-10-10
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING
|
|
45
|
+
|
|
46
|
+
- **Scripted planner responses must use the strict JSON envelope** — `@voltro/plugin-ai-flows`, `@voltro/cli`
|
|
47
|
+
|
|
48
|
+
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.
|
|
49
|
+
- **CMS: experiments planned on a release, and version B for one section** — `@voltro/cms`
|
|
50
|
+
|
|
51
|
+
`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`.
|
|
52
|
+
|
|
53
|
+
**`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).
|
|
54
|
+
- **Live row patches omit unchanged ID order and reuse subscription indexes** — `@voltro/protocol`, `@voltro/client`, `@voltro/runtime`, `@voltro/api-client`, `@voltro/cli`, `@voltro/voltro`
|
|
55
|
+
|
|
56
|
+
`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.
|
|
57
|
+
- **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`
|
|
58
|
+
|
|
59
|
+
`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.
|
|
60
|
+
|
|
61
|
+
**`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).
|
|
62
|
+
- **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`
|
|
63
|
+
|
|
64
|
+
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.
|
|
65
|
+
|
|
66
|
+
**`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).
|
|
67
|
+
- **Histogram boundaries are plain arrays and labels are attributes** — `@voltro/runtime`, `@voltro/plugin-prometheus`, `@voltro/plugin-datadog`
|
|
68
|
+
|
|
69
|
+
`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 }`.
|
|
70
|
+
|
|
71
|
+
**`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).
|
|
72
|
+
- **Postgres workflow runners lock shards in a new key space — no rolling deploy** — `@voltro/workflow`, `@voltro/sql-postgres`, `@voltro/cli`
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
**`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).
|
|
77
|
+
- **Authorize tenant-private browser upload policies** — `@voltro/protocol`, `@voltro/client`, `@voltro/plugin-storage`, `@voltro/cli`
|
|
78
|
+
|
|
79
|
+
`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.
|
|
80
|
+
|
|
81
|
+
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.
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
**`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).
|
|
86
|
+
- **Bound reaction admission work and batch reference cleanup** — `@voltro/runtime`, `@voltro/cli`
|
|
87
|
+
|
|
88
|
+
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.
|
|
89
|
+
|
|
90
|
+
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.
|
|
91
|
+
|
|
92
|
+
**`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).
|
|
93
|
+
- **Completed memory streams have an explicit replay window** — `@voltro/ai`, `@voltro/cli`
|
|
94
|
+
|
|
95
|
+
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.
|
|
96
|
+
|
|
97
|
+
Redis replay verifies list positions and falls back to bounded filtering after sequence gaps, preserving later events and terminal outcomes.
|
|
98
|
+
|
|
99
|
+
**`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).
|
|
100
|
+
- **Fetch bounded SCIM pages and aggregate totals** — `@voltro/plugin-scim`, `@voltro/cli`
|
|
101
|
+
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
**`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).
|
|
105
|
+
- **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`
|
|
106
|
+
|
|
107
|
+
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`.
|
|
108
|
+
|
|
109
|
+
**`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).
|
|
110
|
+
- **Test runners take no abort signal and the test clock moves** — `@voltro/testing`
|
|
111
|
+
|
|
112
|
+
`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`).
|
|
113
|
+
|
|
114
|
+
**`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).
|
|
115
|
+
- **Schema.Uint8Array no longer decodes a number array** — `@voltro/protocol`
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
**`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).
|
|
120
|
+
- **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`
|
|
121
|
+
|
|
122
|
+
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`.
|
|
123
|
+
|
|
124
|
+
**`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).
|
|
125
|
+
- **Workflow definitions are callable and journal exits are reason lists** — `@voltro/workflow`, `@voltro/cli`, `@voltro/plugin-webhooks`
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
|
|
129
|
+
**`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).
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
|
|
133
|
+
- **CMS: AI candidates for whole documents, adopted unit by unit** — `@voltro/cms`
|
|
134
|
+
|
|
135
|
+
`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`.
|
|
136
|
+
- **CMS: bulk into a release, and bulk runs that continue where they stopped** — `@voltro/cms`
|
|
137
|
+
|
|
138
|
+
`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.
|
|
139
|
+
- **Cache identity compiler for delivery behind shared caches** — `@voltro/cms`
|
|
140
|
+
|
|
141
|
+
`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.
|
|
142
|
+
- **CMS: collections and brand portals with capped external downloads** — `@voltro/cms`
|
|
143
|
+
|
|
144
|
+
`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`.
|
|
145
|
+
- **CMS: components say what they are for; the picker finds them by job** — `@voltro/cms`
|
|
146
|
+
|
|
147
|
+
`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`.
|
|
148
|
+
- **CMS: component slots, variants, fixtures and migrations** — `@voltro/cms`
|
|
149
|
+
|
|
150
|
+
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.
|
|
151
|
+
- **CMS: consent vendors and the reader's choice as a browser-enforced policy** — `@voltro/cms`
|
|
152
|
+
|
|
153
|
+
`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`.
|
|
154
|
+
- **CMS: cutover readiness — issues, adoption, criteria, go/fallback ledger** — `@voltro/cms`
|
|
155
|
+
|
|
156
|
+
`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`.
|
|
157
|
+
- **CMS: delivery checks — a publish is done when its public address shows it** — `@voltro/cms`
|
|
158
|
+
|
|
159
|
+
`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`.
|
|
160
|
+
- **CMS: design tokens and actions as component props** — `@voltro/cms`
|
|
161
|
+
|
|
162
|
+
`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`.
|
|
163
|
+
- **A download block in rich text, its file from the media library** — `@voltro/cms`
|
|
164
|
+
|
|
165
|
+
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`.
|
|
166
|
+
|
|
167
|
+
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.
|
|
168
|
+
- **edgePurgeRequests purges pages by address too** — `@voltro/cms`
|
|
169
|
+
|
|
170
|
+
`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.
|
|
171
|
+
- **Purge requests for Cloudflare, Fastly or a webhook, by cache tag** — `@voltro/cms`
|
|
172
|
+
|
|
173
|
+
`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.
|
|
174
|
+
- **CMS: independent copies with their source and revision** — `@voltro/cms`
|
|
175
|
+
|
|
176
|
+
`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`.
|
|
177
|
+
- **CMS: masked inheritance, effective values and their origin, release pins** — `@voltro/cms`
|
|
178
|
+
|
|
179
|
+
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`).
|
|
180
|
+
- **CMS: licence terms — from, territories, channels, purposes; rights ending soon** — `@voltro/cms`
|
|
181
|
+
|
|
182
|
+
`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.
|
|
183
|
+
- **A rich text's link picker learns its block; visual selection as offsets** — `@voltro/cms`
|
|
184
|
+
|
|
185
|
+
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`.
|
|
186
|
+
- **CMS: migration stages with owners and exit gates, record ledger, cutover plan** — `@voltro/cms`
|
|
187
|
+
|
|
188
|
+
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`.
|
|
189
|
+
- **CMS: quality rules form policy packs per site and language; checks see when** — `@voltro/cms`
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
- **CMS: typed presence anchors** — `@voltro/cms`
|
|
193
|
+
|
|
194
|
+
`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`.
|
|
195
|
+
- **CMS: reference graph per perspective and revision; replace references everywhere** — `@voltro/cms`
|
|
196
|
+
|
|
197
|
+
`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.
|
|
198
|
+
- **A release's checkpoint across the addresses of its entries** — `@voltro/cms`
|
|
199
|
+
|
|
200
|
+
`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.
|
|
201
|
+
- **CMS: reviewer suggestions on a workflow step, and overdue steps for reminders** — `@voltro/cms`
|
|
202
|
+
|
|
203
|
+
`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.
|
|
204
|
+
- **CMS: review workflows as a versioned decision graph** — `@voltro/cms`
|
|
205
|
+
|
|
206
|
+
`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`.
|
|
207
|
+
- **Rich-text field profiles, components in text, track changes, typing together** — `@voltro/cms`
|
|
208
|
+
|
|
209
|
+
`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.
|
|
210
|
+
|
|
211
|
+
- 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.
|
|
212
|
+
|
|
213
|
+
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.
|
|
214
|
+
|
|
215
|
+
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.
|
|
216
|
+
- **Selection intents for a component's prop and for a draft not saved yet** — `@voltro/cms`
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
|
|
220
|
+
`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.
|
|
221
|
+
- **Resource selection intents: choose in the tool that owns the resource** — `@voltro/cms`
|
|
222
|
+
|
|
223
|
+
`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 }`.
|
|
224
|
+
- **CMS: site domains verified by a DNS TXT record; a primary domain** — `@voltro/cms`
|
|
225
|
+
|
|
226
|
+
`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`.
|
|
227
|
+
- **CMS: templates rule regions, widths and nesting depth** — `@voltro/cms`
|
|
228
|
+
|
|
229
|
+
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.
|
|
230
|
+
- **A translation export names what it does not carry** — `@voltro/cms`
|
|
231
|
+
|
|
232
|
+
`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.
|
|
233
|
+
- **CMS: metrics as versioned definitions, drillable to revision and release** — `@voltro/cms`
|
|
234
|
+
|
|
235
|
+
`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`.
|
|
236
|
+
- **Comments: thread kinds, status policy, visibility, retention and status history** — `@voltro/plugin-comments`
|
|
237
|
+
|
|
238
|
+
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.
|
|
239
|
+
- **An isr page can render per variant, and send a request past the shared cache** — `@voltro/cli`
|
|
240
|
+
|
|
241
|
+
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.
|
|
242
|
+
- **Built-in durable outboxes store subject partitions atomically** — `@voltro/local-first`
|
|
243
|
+
|
|
244
|
+
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.
|
|
245
|
+
- **Static pages read the api at build time when its origin is set** — `@voltro/cli`
|
|
246
|
+
|
|
247
|
+
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.
|
|
248
|
+
- **Storage: editImage — creative edits that make a new version** — `@voltro/plugin-storage`
|
|
249
|
+
|
|
250
|
+
`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.
|
|
251
|
+
- **Storage: bound how long a cached public object outlives turning private** — `@voltro/plugin-storage`
|
|
252
|
+
|
|
253
|
+
`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.
|
|
254
|
+
- **A deferred webhook emit records its outbox row id as the event id** — `@voltro/plugin-webhooks`
|
|
255
|
+
|
|
256
|
+
`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.
|
|
257
|
+
|
|
258
|
+
### Changed
|
|
259
|
+
|
|
260
|
+
- **Aggregates and memory JOINs prepare their work once per evaluation** — `@voltro/runtime`
|
|
261
|
+
|
|
262
|
+
Materialize aggregate groups directly and prepare memory JOIN qualification, null rows and supported scalar ON membership within each evaluation.
|
|
263
|
+
- **Prepare wide array predicates and migration diagnostics** — `@voltro/database`
|
|
264
|
+
|
|
265
|
+
Prepare wide ordinary in-memory array predicates and drifted migration diagnostics locally while preserving native membership, current files and ledger identity.
|
|
266
|
+
- **Reduce fuzzy matching and repeated feed date parsing** — `@voltro/plugin-search`, `@voltro/content`, `@voltro/database`
|
|
267
|
+
|
|
268
|
+
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.
|
|
269
|
+
- **Bound pending log sends and finish shutdown after a deadline** — `@voltro/logger`, `@voltro/plugin-logship`, `@voltro/plugin-datadog`
|
|
270
|
+
|
|
271
|
+
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.
|
|
272
|
+
- **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`
|
|
273
|
+
|
|
274
|
+
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.
|
|
275
|
+
|
|
276
|
+
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.
|
|
277
|
+
- **Reduce build, content and locale-resolution allocations** — `@voltro/cli`, `@voltro/content`, `@voltro/i18n`
|
|
278
|
+
|
|
279
|
+
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.
|
|
280
|
+
- **Production server bundles drop chunks nothing can load** — `@voltro/cli`
|
|
281
|
+
|
|
282
|
+
`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.
|
|
283
|
+
- **Inspect nested ICU placeholders with less repeated work** — `@voltro/i18n`
|
|
284
|
+
|
|
285
|
+
Prepare repeated nested ICU brace scans and long catalog messages locally while preserving placeholder order, quoting and live catalog reads.
|
|
286
|
+
- **Release paired CDC expiry timers immediately** — `@voltro/sql-mysql`
|
|
287
|
+
|
|
288
|
+
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.
|
|
289
|
+
- **Faster schema-derived controls and repeated snapshot lookups** — `@voltro/client`
|
|
290
|
+
|
|
291
|
+
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.
|
|
292
|
+
- **Delivery checks on the pages that include a document** — `@voltro/cms`
|
|
293
|
+
|
|
294
|
+
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.
|
|
295
|
+
- **A link's picker can hand the choosing to the page tree alone** — `@voltro/cms`
|
|
296
|
+
|
|
297
|
+
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.
|
|
298
|
+
- **A draft save that changes nothing makes no revision** — `@voltro/cms`
|
|
299
|
+
|
|
300
|
+
`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.
|
|
301
|
+
- **Cold SSR compile queues drain without repeated tail movement** — `@voltro/cli`
|
|
302
|
+
|
|
303
|
+
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.
|
|
304
|
+
- **Share resolved subscription reads within committed bulk buffers** — `database`, `runtime`, `sql-postgres`, `sql-mysql`, `sql-sqlite`, `sql-mssql`
|
|
305
|
+
|
|
306
|
+
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.
|
|
307
|
+
- **Reduce repeated work in core data and observability paths** — `@voltro/protocol`, `@voltro/database`, `@voltro/runtime`, `@voltro/cli`, `@voltro/sql-postgres`, `@voltro/sql-mysql`
|
|
308
|
+
|
|
309
|
+
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.
|
|
310
|
+
|
|
311
|
+
CDC echoes also preserve procedure and agent attribution, including identity with no trace or subject.
|
|
312
|
+
- **Large row deliveries share repeated relation implication work** — `@voltro/runtime`
|
|
313
|
+
|
|
314
|
+
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.
|
|
315
|
+
- **Reduce repeated CSS graph and SPA layout work** — `@voltro/cli`, `@voltro/web`
|
|
316
|
+
|
|
317
|
+
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.
|
|
318
|
+
- **Snapshots, boot diagnostics and log sinks build fewer temporary collections** — `@voltro/runtime`, `@voltro/logger`
|
|
319
|
+
|
|
320
|
+
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.
|
|
321
|
+
- **Reduce validation work for broad middleware route lists** — `@voltro/cli`
|
|
322
|
+
|
|
323
|
+
Startup validation prepares ordinary exact route lists once while preserving overlap witnesses, subtree exclusions and asset scope.
|
|
324
|
+
- **Prepare large form error membership and stable accessibility prefixes** — `@voltro/client`
|
|
325
|
+
|
|
326
|
+
Prepare large form error membership and stable accessibility prefixes; bound rich-text cardinality work and avoid fenced-code tail copies.
|
|
327
|
+
- **Reduce frontend and code-generation collections** — `@voltro/client`, `@voltro/web`, `@voltro/cli`
|
|
328
|
+
|
|
329
|
+
Reduce transient frontend and code-generation collections while preserving transport observations, 404 loader behavior, image candidate order and generated declaration contracts.
|
|
330
|
+
- **Client, web and CLI hot paths build fewer temporary collections** — `@voltro/client`, `@voltro/web`, `@voltro/cli`, `@voltro/local-first`
|
|
331
|
+
|
|
332
|
+
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.
|
|
333
|
+
- **Indexed queues and local queries avoid whole-cache work** — `@voltro/client`, `@voltro/local-first`, `@voltro/web`
|
|
334
|
+
|
|
335
|
+
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.
|
|
336
|
+
|
|
337
|
+
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.
|
|
338
|
+
- **A page that renders per variant tells a cache in front what it may keep** — `@voltro/cli`
|
|
339
|
+
|
|
340
|
+
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.
|
|
341
|
+
- **Reduce workflow journal scanning and gateway queue movement** — `@voltro/workflow`, `@voltro/runtime`
|
|
342
|
+
|
|
343
|
+
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.
|
|
344
|
+
- **Reduce replay retention and eager projection planning work** — `@voltro/runtime`, `@voltro/client`, `@voltro/database`
|
|
345
|
+
|
|
346
|
+
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.
|
|
347
|
+
- **Local queries build their string sort specification once** — `@voltro/client`
|
|
348
|
+
|
|
349
|
+
Avoid rebuilding string-column sort specifications per local-query comparison, preserving stable and custom ordering.
|
|
350
|
+
- **Share source preparation across localized content views** — `@voltro/content`
|
|
351
|
+
|
|
352
|
+
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.
|
|
353
|
+
- **Reduce intermediate allocation in memory reads and runtime diagnostics** — `@voltro/runtime`, `@voltro/cli`
|
|
354
|
+
|
|
355
|
+
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.
|
|
356
|
+
- **Reduce nullable field and pending outbox allocation** — `@voltro/client`
|
|
357
|
+
|
|
358
|
+
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.
|
|
359
|
+
- **External ID metadata also describes local polymorphic targets** — `@voltro/database`
|
|
360
|
+
|
|
361
|
+
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.
|
|
362
|
+
- **Form length bounds and capability scopes are prepared once per check** — `@voltro/client`
|
|
363
|
+
|
|
364
|
+
Prepare wide form length-bound metadata per validation and large declared capability scope membership per decision, retaining nested/localized errors and unknown resource authority.
|
|
365
|
+
- **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`
|
|
366
|
+
|
|
367
|
+
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.
|
|
368
|
+
|
|
369
|
+
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.
|
|
370
|
+
- **Prepare membership, authorization and migration matching per operation** — `@voltro/plugin-scim`, `@voltro/protocol`, `@voltro/database`, `@voltro/plan`
|
|
371
|
+
|
|
372
|
+
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.
|
|
373
|
+
- **Prepare broad public API routes once** — `@voltro/protocol`
|
|
374
|
+
|
|
375
|
+
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.
|
|
376
|
+
- **Prepare release and database reads without repeated scans** — `@voltro/plugin-releases`, `@voltro/database`, `@voltro/runtime`
|
|
377
|
+
|
|
378
|
+
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.
|
|
379
|
+
|
|
380
|
+
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.
|
|
381
|
+
- **Row decoding and flat-row comparison avoid redundant cell work** — `@voltro/database`, `@voltro/protocol`
|
|
382
|
+
|
|
383
|
+
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.
|
|
384
|
+
- **SQLite named composite UNIQUE no longer re-issues unchanged DDL** — `@voltro/database`
|
|
385
|
+
|
|
386
|
+
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.
|
|
387
|
+
|
|
388
|
+
Preserve same-name composite UNIQUE constraints during verified column renames without redundant drop/add work.
|
|
389
|
+
- **Runtime snapshots copy less; consumed broadcast payloads are released** — `@voltro/runtime`, `@voltro/local-first`, `@voltro/plugin-broadcast`
|
|
390
|
+
|
|
391
|
+
Reduce temporary runtime snapshot collections and release consumed broadcast payload references while preserving ordered delivery, replay, connection teardown and offline lineage behavior.
|
|
392
|
+
- **Reduce reactive form and loader bookkeeping allocations** — `@voltro/client`, `@voltro/runtime`, `@voltro/web`
|
|
393
|
+
|
|
394
|
+
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.
|
|
395
|
+
- **Reduce reactive fan-out and workflow inspection overhead** — `@voltro/runtime`, `@voltro/workflow`, `@voltro/local-first`
|
|
396
|
+
|
|
397
|
+
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.
|
|
398
|
+
- **Subscription routing scales with active index definitions** — `@voltro/runtime`
|
|
399
|
+
|
|
400
|
+
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.
|
|
401
|
+
- **Combine replica traces with linear metadata scans** — `@voltro/protocol`
|
|
402
|
+
|
|
403
|
+
Select earliest replica traces and accumulate trace metadata with bounded linear passes while retaining span ordering and partial observation evidence.
|
|
404
|
+
- **Share running request-loader reads** — `@voltro/runtime`
|
|
405
|
+
|
|
406
|
+
Request-scoped loaders share overlapping in-flight reads while preserving batching, positional results, isolation and failure retries.
|
|
407
|
+
- **A cached REST answer can decide who may share it; failures are never shared** — `@voltro/protocol`
|
|
408
|
+
|
|
409
|
+
`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.
|
|
410
|
+
|
|
411
|
+
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.
|
|
412
|
+
- **Reduce captured runtime and form collection overhead** — `@voltro/runtime`, `@voltro/client`
|
|
413
|
+
|
|
414
|
+
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.
|
|
415
|
+
- **Runtime diagnostics, mounts and savepoints build fewer temporary collections** — `@voltro/runtime`
|
|
416
|
+
|
|
417
|
+
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.
|
|
418
|
+
- **Reduce routing and burst bookkeeping overhead** — `@voltro/runtime`
|
|
419
|
+
|
|
420
|
+
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.
|
|
421
|
+
- **Raw reads, updates and in-memory full-text prefixes are prepared once** — `@voltro/runtime`
|
|
422
|
+
|
|
423
|
+
Prepare wide immutable raw-read identity, explicit update membership and large in-memory full-text prefixes without retaining mutable decisions or changing authority.
|
|
424
|
+
- **Cheaper cleanup for shared pending scripts** — `@voltro/web`
|
|
425
|
+
|
|
426
|
+
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.
|
|
427
|
+
- **Reuse Intl formatters and batch comment lists** — `@voltro/i18n`, `@voltro/cache`, `@voltro/plugin-comments`, `@voltro/cli`
|
|
428
|
+
|
|
429
|
+
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.
|
|
430
|
+
- **Snapshot restore and foreign-key metadata are read once per boundary** — `@voltro/database`
|
|
431
|
+
|
|
432
|
+
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.
|
|
433
|
+
- **Reduce repeated work while scanning source trees** — `@voltro/cli`
|
|
434
|
+
|
|
435
|
+
Avoid recursive source-file result copies and repeated ordinary exclusion-list validation while preserving mutable filters, traversal order and vanished-directory handling.
|
|
436
|
+
- **Reduce subscription metadata and idle event-route selection work** — `@voltro/client`, `@voltro/runtime`
|
|
437
|
+
|
|
438
|
+
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.
|
|
439
|
+
- **Bound upload progress and snapshot dependency planning costs** — `@voltro/client`, `@voltro/i18n`, `@voltro/cli`
|
|
440
|
+
|
|
441
|
+
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.
|
|
442
|
+
- **Reduce repeated searches in broad migration plans** — `@voltro/database`
|
|
443
|
+
|
|
444
|
+
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.
|
|
445
|
+
- **Wide SQL projections prepare repeated compiler lookups** — `@voltro/database`
|
|
446
|
+
|
|
447
|
+
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.
|
|
448
|
+
- **Reduce transient wire and route preparation collections** — `@voltro/protocol`
|
|
449
|
+
|
|
450
|
+
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.
|
|
451
|
+
- **Reduce repeated schema work in writes, privacy graphs and field visibility** — `@voltro/database`, `@voltro/runtime`
|
|
452
|
+
|
|
453
|
+
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.
|
|
454
|
+
|
|
455
|
+
### Fixed
|
|
456
|
+
|
|
457
|
+
- **voltro build resolves an unset NODE_ENV to production** — `@voltro/cli`
|
|
458
|
+
|
|
459
|
+
`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.
|
|
460
|
+
- **Reuse client lookup metadata and bound URL-prefix checks** — `@voltro/client`
|
|
461
|
+
|
|
462
|
+
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.
|
|
463
|
+
- **A component in a rich text records its reference props as edges** — `@voltro/cms`
|
|
464
|
+
|
|
465
|
+
`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.
|
|
466
|
+
- **CMS: a field typed before its shared text arrived is no longer lost on save** — `@voltro/cms`, `@voltro/local-first`
|
|
467
|
+
|
|
468
|
+
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.
|
|
469
|
+
- **CMS: a unique field's duplicate is refused at its field, not by the database** — `@voltro/cms`
|
|
470
|
+
|
|
471
|
+
`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.
|
|
472
|
+
- **Content collections resolve their lazy server entry in native Node** — `@voltro/content`
|
|
473
|
+
|
|
474
|
+
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.
|
|
475
|
+
- **CRDT history uses a short framework-owned index name** — `@voltro/runtime`
|
|
476
|
+
|
|
477
|
+
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.
|
|
478
|
+
- **db branch: a plain and a partial index on the same columns keep their own names** — `@voltro/database`, `@voltro/cli`
|
|
479
|
+
|
|
480
|
+
`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`).
|
|
481
|
+
- **Every @effect/* package the framework uses is pinned exactly** — `@voltro/runtime`, `@voltro/cli`
|
|
482
|
+
|
|
483
|
+
`@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.
|
|
484
|
+
- **The Effect 4 codemod leaves your own Left / Right unions and Type lookups alone** — `@voltro/cli`
|
|
485
|
+
|
|
486
|
+
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.
|
|
487
|
+
- **Env values are shared by every copy of @voltro/env in a process** — `@voltro/env`
|
|
488
|
+
|
|
489
|
+
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.
|
|
490
|
+
- **The wildcard event descriptor matches guarded events again** — `@voltro/protocol`
|
|
491
|
+
|
|
492
|
+
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.
|
|
493
|
+
- **A bundled gRPC surface serves grpc.health.v1 and reflection again** — `@voltro/cli`
|
|
494
|
+
|
|
495
|
+
`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.
|
|
496
|
+
- **Reduce live table rendering and release subscription buffers** — `@voltro/ui`, `@voltro/client`, `@voltro/runtime`
|
|
497
|
+
|
|
498
|
+
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.
|
|
499
|
+
- **A MySQL file migration keeps to two connections** — `@voltro/database`
|
|
500
|
+
|
|
501
|
+
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`.
|
|
502
|
+
- **Number fields keep their bounds and formField metadata on Effect 4** — `@voltro/protocol`, `@voltro/client`
|
|
503
|
+
|
|
504
|
+
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.
|
|
505
|
+
- **One vitest peer range across the framework; voltro update aligns it again** — `@voltro/workflow`
|
|
506
|
+
|
|
507
|
+
`@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.
|
|
508
|
+
- **voltro add patches pins Effect's transitive companion to the effect version** — `@voltro/cli`
|
|
509
|
+
|
|
510
|
+
`@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.
|
|
511
|
+
- **Preserve optimized work and lifetimes on Effect 4** — `@voltro/ai`, `@voltro/client`, `@voltro/cli`, `@voltro/runtime`, `@voltro/protocol`
|
|
512
|
+
|
|
513
|
+
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.
|
|
514
|
+
- **pgvector columns decode on the Effect 4 postgres driver** — `@voltro/sql-postgres`
|
|
515
|
+
|
|
516
|
+
`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.
|
|
517
|
+
- **Plugin references retain their orphan policy through column modifiers** — `@voltro/database`, `@voltro/runtime`
|
|
518
|
+
|
|
519
|
+
`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.
|
|
520
|
+
|
|
521
|
+
Orphan cleanup uses the same predicate constructor as native SQL stores, so registered delete/null policies also execute against real databases.
|
|
522
|
+
- **Concurrent first callback upserts of one key all land on postgres** — `@voltro/sql-postgres`
|
|
523
|
+
|
|
524
|
+
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.
|
|
525
|
+
- **A workflow's rateLimit() counts the run once, not once per replay** — `@voltro/workflow`
|
|
526
|
+
|
|
527
|
+
`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}`).
|
|
528
|
+
- **voltro serverless build . bundles the functions in that directory** — `@voltro/cli`
|
|
529
|
+
|
|
530
|
+
`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.
|
|
531
|
+
- **SQLite decimal review lists executable declarations for relevant stores** — `@voltro/cli`
|
|
532
|
+
|
|
533
|
+
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.
|
|
534
|
+
- **Retry SQLite transactions after a concurrent WAL snapshot change** — `@voltro/database`
|
|
535
|
+
|
|
536
|
+
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.
|
|
537
|
+
- **A page render stops presenting a session the api rejected** — `@voltro/cli`
|
|
538
|
+
|
|
539
|
+
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.
|
|
540
|
+
- **Startup reuses source inventories and clears completed teardown deadlines** — `@voltro/cli`, `@voltro/runtime`
|
|
541
|
+
|
|
542
|
+
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.
|
|
543
|
+
|
|
544
|
+
Compiled API production shutdown now calls the shared runner's actual shutdown handle, so registered startup cleanup also runs on graceful termination.
|
|
545
|
+
- **A derivative is never more open than its original** — `@voltro/plugin-storage`
|
|
546
|
+
|
|
547
|
+
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.
|
|
548
|
+
- **Encrypted transfers release input streams and avoid repeated buffer copies** — `@voltro/data-transfer`, `@voltro/plugin-openapi`
|
|
549
|
+
|
|
550
|
+
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.
|
|
551
|
+
- **An undeclared input key is refused inside class and transformed inputs too** — `@voltro/protocol`
|
|
552
|
+
|
|
553
|
+
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.
|
|
554
|
+
- **Summary metric snapshots use fewer temporary arrays** — `@voltro/runtime`
|
|
555
|
+
|
|
556
|
+
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.
|
|
557
|
+
- **Timeline preparation follows the recording mode** — `@voltro/cli`
|
|
558
|
+
|
|
559
|
+
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.
|
|
560
|
+
- **Optional Timeline failures preserve successful schema maintenance** — `@voltro/cli`
|
|
561
|
+
|
|
562
|
+
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.
|
|
563
|
+
- **Turso stores share the transaction key with every other SQL client** — `@voltro/sql-turso`
|
|
564
|
+
|
|
565
|
+
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.
|
|
566
|
+
- **voltro update aligns catalogs and writes ranges YAML can read** — `@voltro/cli`
|
|
567
|
+
|
|
568
|
+
`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.
|
|
569
|
+
- **voltro update aligns peers installed under a workspace member and in overrides** — `@voltro/cli`
|
|
570
|
+
|
|
571
|
+
`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.
|
|
572
|
+
|
|
573
|
+
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`.
|
|
574
|
+
- **voltro update --wait asks the registry the project installs from** — `@voltro/cli`
|
|
575
|
+
|
|
576
|
+
`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.
|
|
577
|
+
- **voltro update runs the source transforms in every framework workspace member** — `@voltro/cli`
|
|
578
|
+
|
|
579
|
+
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.
|
|
580
|
+
|
|
581
|
+
### Internal (no consumer-facing effect)
|
|
582
|
+
|
|
583
|
+
- **Prepare file-safe UI installation and update internals** — `@voltro/cli`
|
|
584
|
+
|
|
585
|
+
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.
|
|
586
|
+
|
|
587
|
+
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.
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## [0.84.0] — 2026-10-07
|
|
592
|
+
|
|
593
|
+
### ⚠ BREAKING
|
|
594
|
+
|
|
595
|
+
- **CRDT documents hard-compact into epochs without losing or doubling edits** — `@voltro/local-first`, `@voltro/runtime`, `@voltro/cli`
|
|
596
|
+
|
|
597
|
+
Past `crdt.rebaseMaxBytes` (default 4 MiB, `0` = never) the write path rewrites a document as a new epoch — same content, no history — after archiving the old lineage as a version. Subscriptions replace the cell instead of sending an increment across epochs, `createSyncClient` and the CRDT hooks replace their local state instead of merging two lineages (`onEpochChange`), and an update made on an archived lineage is carried over for text (`CrdtBackend.translate`) or refused as `crdt.staleLineage` for structured content, never merged into a doubled document. `useCrdtDoc` folds unsent edits restored after a reload into the document it renders. Migration: `rebaseText(state)` is replaced by `rebase(state, epoch)`, and a custom `CrdtBackend` implements `restoreTo`, `rebase`, `epochOf`, `authorsOf`, `translate`, `counterValue` and `counterAdd` (text handles add `undo`/`redo`, doc handles `epoch()`, `schemaVersion()` and `setSchemaVersion(version)`) — the `0.84.0/06_crdt-backend-epochs-and-history` codemod lists the affected files. Env override: `VOLTRO_CRDT_REBASE_MAX_BYTES`.
|
|
598
|
+
- **Last-write-wins orders by hybrid logical clock stamps** — `@voltro/local-first`
|
|
599
|
+
|
|
600
|
+
`createHybridClock({ node })` issues stamps that never run backwards and order a write made after seeing another one after it, even when the two devices' clocks disagree — ordered by wall clocks, the device whose clock runs fast wins every race. `FieldValue.updatedAt` is now `number | HlcStamp`, and `lastWriteWins` compares stamps causally (a plain millisecond epoch still works and compares as that millisecond). `receive(stamp)` folds a remote stamp in; `maxDriftMs` refuses one from a clock too far ahead instead of adopting it. Migration: writing a number still compiles; code that reads `updatedAt` as a number must narrow it — the `0.84.0/05_field-value-updated-at-accepts-hlc-stamps` codemod lists the affected files.
|
|
601
|
+
- **Retry Inspect creation with a stable request identity** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
602
|
+
|
|
603
|
+
Inspect row creation requires a stable `requestId` alongside `table` and `row`. Creation and its receipt commit atomically; retrying unchanged input with the same identity returns the original record identity without inserting again. A changed payload with that identity returns HTTP 409. Responses contain the creation receipt and a current row observation, which can be null if the row was deleted.
|
|
604
|
+
|
|
605
|
+
The shared data explorer retains this identity while its creation dialog stays open, and both dashboard adapters forward it. Custom `DataViewerPage.useInsertRow` providers must forward the supplied identity unchanged. Custom Inspect callers must allocate one identity per intended creation and reuse it on uncertain retries. The shared local and Cloud data explorer now persists metadata-only creation identities across dialog closure and reload, observes without replay, and requires an acknowledged created or withdrawn outcome before another creation in that table. Custom insert providers must return the matching receipt instead of void; connect creationRecovery for persistent recovery. Both hosts wire read-only observations and terminal withdrawal to the same mutation gateway.
|
|
606
|
+
|
|
607
|
+
Development Inspect also exposes read-only creation observations and an authenticated withdrawal endpoint. Withdrawal atomically fences delayed creation when it wins, or reports an already committed record without deleting it. A missing receipt is not treated as proof that no request can still complete.
|
|
608
|
+
|
|
609
|
+
Shared dashboard hosts can validate matching creation observations and derive a mutation-gateway recovery scope through the exported creation contract helpers. The metadata journal persists no row payloads and refuses dispatch when storage or browser locking is unavailable; both hosts connect it to the shared visible recovery actions.
|
|
610
|
+
|
|
611
|
+
**`voltro update` carries you across this** — codemod `0.84.0/02_inspect-creation-intent`. 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.84.0).
|
|
612
|
+
- **outboxPersistence keeps one queue per signed-in subject** — `@voltro/local-first`
|
|
613
|
+
|
|
614
|
+
`outboxPersistence(adapter, now)` is now `outboxPersistence(adapter, { now?, partition? })`. With `partition` (for example a mirror source's `partitionKey`), each queued write is stored under the subject that made it: `load` returns only the current subject's writes and `save` replaces only those, so a write queued before a sign-out is never replayed under the next person's identity and waits for its own subject to sign in again. Saves are serialised. Migration: the `0.84.0/04_outbox-persistence-takes-options` codemod rewrites a positional clock into `{ now }`.
|
|
615
|
+
- **Reject stale release confirmations across replicas** — `@voltro/plugin-releases`, `@voltro/devtools-ui`, `@voltro/cli`
|
|
616
|
+
|
|
617
|
+
Release confirmations carry the exact target ID and observed revision. A stale confirmation is rejected with HTTP 409 even when the dashboard has not received the concurrent change yet. Every management write advances the target revision inside the same transaction as release state and its audit event; rejected transactions leave all three unchanged. Management mutations with an ambiguous app name must select a target instead of silently modifying the first platform.
|
|
618
|
+
|
|
619
|
+
The declarative schema adds a revision column with default zero to existing release targets. Automation can supply `expectedTarget: { id, revision }` from the Inspect overview; single-target calls may omit the expectation.
|
|
620
|
+
|
|
621
|
+
`voltro release` accepts `--target-id` together with `--expected-revision` on management actions. It preserves these values through dry runs and committed requests, refuses partial/invalid expectations before artefact upload, and never refreshes a stale expectation automatically. CLI rollout percentages now match the server's whole-number requirement.
|
|
622
|
+
|
|
623
|
+
Release events identify their platform target and channel in the dashboard. Custom event providers must preserve the nullable `channelId` and `channelName` fields. Missing target or channel metadata remains identifiable by its stored ID.
|
|
624
|
+
|
|
625
|
+
**`voltro update` carries you across this** — codemod `0.84.0/03_release-target-confirmations`. 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.84.0).
|
|
626
|
+
- **SQLite decimal columns use exact fixed-point storage** — `@voltro/database`, `@voltro/cli`
|
|
627
|
+
|
|
628
|
+
`decimal()` and `numeric()` now use exact encoded TEXT storage on SQLite, native Turso and libSQL. Existing NUMERIC columns are refused before application readiness, including when automatic migration is disabled. Migration: stop all readers and writers, back up and review existing values, then publish an explicit offline conversion through the server-only database conversion API and a nontransactional `before-schema` file migration. Review custom SQL and external readers for the physical format change; lost precision in existing NUMERIC data cannot be reconstructed automatically. Native decimal storage on PostgreSQL, MySQL, MariaDB and MSSQL is unchanged.
|
|
629
|
+
|
|
630
|
+
**`voltro update` carries you across this** — codemod `0.84.0/01_sqlite-decimals-use-exact-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.84.0).
|
|
631
|
+
- **Direct storage metadata counter rows use decimal strings** — `@voltro/plugin-storage`, `@voltro/cli`
|
|
632
|
+
|
|
633
|
+
The exported `storageObjectLifecycleTables` descriptor now types its shared-object `reservations` counter as a decimal string because the column uses `bigint()`. Review custom framework-metadata reads and writes for exact integer handling; the manual upgrade note identifies direct descriptor and raw-table references. The supported storage service still exposes numeric sizes and usage within `Number.MAX_SAFE_INTEGER`. Reconcile existing framework tables through the normal schema plan/apply workflow; no database changes or source rewrites are performed by the note.
|
|
634
|
+
|
|
635
|
+
**`voltro update` carries you across this** — codemod `0.84.0/03_storage-metadata-bigint`. 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.84.0).
|
|
636
|
+
|
|
637
|
+
### Added
|
|
638
|
+
|
|
639
|
+
- **The query mirror plugs into the api mount and derives what to mirror** — `@voltro/client`, `@voltro/web`, `@voltro/react-native`, `@voltro/cli`, `@voltro/local-first`
|
|
640
|
+
|
|
641
|
+
The local-first query mirror could be built but not handed to the subscription cache, so offline reads and delta-resume after a reload never ran in an app. Declare it on the api in the web app's `app.config.ts` as `apis.<name>.mirror: createMirrorSource({ … })` (on React Native, `mirror` on the `MobileApiBinding`). The runtime resolves the source once per connection generation and hands it the api's descriptors: without `tags`, every query whose `source` tables are all `localFirst()` tables (per the generated `localFirstTables` passed as `metadata`) is mirrored, and a query that also reads a table that did not opt in is not. `remember({ subjectId, tenantId })` after a sign-in stores who is signed in on the device, so a cold start without network still renders that subject's rows; `forget()` on sign-out purges them. `warm: [[tag, input]]` keeps queries subscribed for the whole generation, so they are readable offline before any screen opened them; `warm: 'all'` does that for every query of the sync set whose input accepts `{}`. A source that throws mirrors nothing, the connection still comes up, and the failure is recorded as a `runtime.mirror` client diagnostic. The client-side golden churn is additive: an optional parameter and new optional fields.
|
|
642
|
+
- **queryOnce is importable without the React hooks** — `@voltro/client`, `@voltro/workflow`, `@voltro/database`, `@voltro/cli`
|
|
643
|
+
|
|
644
|
+
`@voltro/client/query` exports `queryOnce` (a query's first value over an api handle) without pulling the package root's React hooks, so a Node or tooling consumer can call it. `@voltro/workflow` now exports `reserveWorkflowRestartInTransaction` beside its sibling `withdrawWorkflowRestartInTransaction`. Internally, tests that reached into sibling packages' source now import their published entry points, which makes the cli and database typechecks clean.
|
|
645
|
+
- **Compose baselines support optional production environment variables** — `@voltro/cli`
|
|
646
|
+
|
|
647
|
+
Declare per-app `optionalProductionEnv` names in `project.json` and run `voltro baseline sync` to pass project-prefixed host variables into production API and web services without requiring them on every deployment. Unset or empty values become empty strings, so a field-encryption previous key can be supplied only during rotation. `productionSecrets` stays required; optional declarations cannot override required or baseline-owned bindings.
|
|
648
|
+
- **A conformance suite makes any CRDT backend a drop-in** — `@voltro/testing`
|
|
649
|
+
|
|
650
|
+
`crdtBackendConformance(name, { backend })` from `@voltro/testing/crdt` registers the executable contract every `CrdtBackend` must meet — merge commutativity, idempotence and identity, collaborative undo, the remote/local fold signal, incremental updates, queued local updates coalescing without loss, compaction keeping mergeability, `restoreTo`, `rebase` with epochs and fresh authors, `translate` across lineages, the content schema version surviving a rebase and commuting counters. The Yjs and Loro backends pass it; an Automerge or in-house backend that passes it plugs into the server merge, the sync client, the hooks, history and counters unchanged.
|
|
651
|
+
- **CRDT documents migrate their content when the editor schema changes** — `@voltro/local-first`, `@voltro/runtime`, `@voltro/cli`
|
|
652
|
+
|
|
653
|
+
`crdt.migrations` in the api's `app.config.ts` declares, per table and CRDT column, versioned `{ version, migrate(doc) }` steps. The write path runs a document's due steps in order inside the same compare-and-swap as the merge, so each document is migrated exactly once however many writes or replicas race, and the migration syncs to clients as an ordinary edit that merges with edits made against the old content. `migrateCrdtColumn(store, { table, column, batchSize? })` from `@voltro/runtime` migrates every document still behind and returns `{ scanned, migrated }`. Each document records its version (`crdtSchemaVersionOf`), which survives hard compaction. `renameEditorNodes(doc, { from, to, field? })` from `@voltro/local-first` (server-safe, no Tiptap) renames an editor node type on Yjs and Loro, keeping attributes and children. On Loro the node keeps its identity; on Yjs, where a node is replaced by a copy, the rename records what replaced what and the write path carries an edit a client made offline against the original — text, deletions, formatting, attributes, new child nodes — into the copy, once (`CrdtBackend.carryReplaced`, optional for custom backends); `applyCrdtMigrations` and `latestCrdtSchemaVersion` are exported for custom tooling.
|
|
654
|
+
- **A CRDT panel shows documents approaching their limits in both dashboards** — `@voltro/cli`, `@voltro/devtools-ui`, `@voltro/runtime`
|
|
655
|
+
|
|
656
|
+
`GET /_voltro/inspect/crdt` (both boot paths, inspect-token protected) reports, per `crdtText()` / `crdtDoc()` / `crdtCounter()` / `crdtSealed()` column, the largest documents with their size, how close each is to the next server limit, their epoch, how many writer sessions their history carries and how many archived versions they keep, plus the limits in force — sizes only, never document content. `@voltro/devtools-ui` renders it as `CrdtPage` (a new `crdt` inspect feature), shown by the local devtools dashboard and the Cloud dashboard. `describeCrdtState` (`@voltro/runtime`) is the size-only reader behind it.
|
|
657
|
+
- **Loro ships as a second CRDT backend** — `@voltro/local-first`, `@voltro/runtime`, `@voltro/cli`, `@voltro/testing`
|
|
658
|
+
|
|
659
|
+
`loroBackend` from `@voltro/local-first/loro` (with `loro-crdt` as an optional peer dependency) is a complete `CrdtBackend` and passes the same conformance suite as Yjs. Select it on both sides: `crdt: { backend: loroBackend }` in the api's `app.config.ts` for the server merge, history, epochs and inspection, and `setDefaultCrdtBackend(loroBackend)` in the client entry. `useCrdtText`, `useCrdtDoc`, `useCrdtCounter`, sealed columns, version history, epochs and content migrations work on it unchanged. The two backends' bytes do not mix: an update from the other backend is refused as `crdt.updateNotDecodable`. A Loro change depends on everything its writer had seen, so client edits travel up as whole document states and an update missing its dependencies is refused instead of being merged without them. The rich-text editor runs on Loro too: `useCrdtEditor` takes a `binding` (`yjsEditorBinding` by default, `loroEditorBinding` from `@voltro/local-first/loro-editor` over `loro-prosemirror`), with carets over the same awareness transport and collaborative undo on both. The Loro binding gives an empty document the schema's skeleton as a change every client writes byte-identically, so two people typing into a new document offline both keep their text. A binding refuses a document of the other backend with an error naming the right one.
|
|
660
|
+
- **crdtSealed() columns keep collaborative content end-to-end encrypted** — `@voltro/database`, `@voltro/runtime`, `@voltro/local-first`
|
|
661
|
+
|
|
662
|
+
A `crdtSealed()` column stores an append-only log of CRDT updates the client encrypted with AES-GCM under a key the server never sees (`generateSealKey` / `importSealKey` / `exportSealKey`). `useCrdtText`, `useCrdtDoc` and `useCrdtCounter` take `seal: { key }`: writes leave the client sealed, delivered logs are opened and merged locally, a frame that does not authenticate reports through `onSealError`, and edits not yet sent are kept sealed in the device's storage too. The clients compact the log, since only they can read it: softly once it outgrows `compactAboveBytes` (a sealed snapshot replacing the frames it covers, keeping every frame appended meanwhile), and hard once the document outgrows `rebaseAboveBytes` (default 4 MiB) — rebased into a new epoch and replacing the whole log, which the server archives as a `rebase` version. Each frame names its lineage epoch in cleartext, so the server appends only frames of the current epoch and refuses older ones as `crdt.staleLineage`; a client that typed offline on the archived lineage carries its edit over itself. A snapshot whose covered bytes changed meanwhile is refused as `crdt.sealedSnapshotStale`, so two clients compacting at once never cut each other's frames. The server checks framing and size limits only, appends with the same compare-and-swap as open folds, and refuses a malformed frame as `crdt.sealedFrameInvalid`; `restoreCrdtVersion` refuses a sealed column as `crdt.sealedRestoreIsClientSide`. `openSealedLog`, `sealUpdate`, `sealSnapshot`, `sealHardCompaction` and the log codec (`applySealedFrame`, `decodeSealedLog`, `sealedLogEpoch`) are exported for custom bindings.
|
|
663
|
+
- **Sealed CRDT versions restore on the client, and the CRDT hooks gain restore()** — `@voltro/runtime`, `@voltro/local-first`
|
|
664
|
+
|
|
665
|
+
`prepareSealedCrdtRestore(store, { versionId })` is the server's half of restoring a `crdtSealed()` cell: it archives the current log as a `restore` version, so the restore stays undoable, and returns the version's ciphertext. The client opens it with `openSealedLog` and passes the plaintext to the new `restore(target)` of `useCrdtText` or `useCrdtDoc`, which writes the restore as an ordinary edit — merged with concurrent edits and sealed like typing. An open column's history bytes never leave the server: `prepareSealedCrdtRestore` refuses one with `crdt.openRestoreIsServerSide`, and `restoreCrdtVersion` restores it on the server as before.
|
|
666
|
+
- **Collaborative undo for useCrdtText and a converging crdtCounter() column** — `@voltro/local-first`, `@voltro/database`
|
|
667
|
+
|
|
668
|
+
`useCrdtText` returns `undo()` / `redo()` / `canUndo` / `canRedo`: they revert this user's own recent edits (typing within `undoCaptureMs` is one step) and never a peer's, and the reverting change syncs like typing. `crdtCounter()` is a column that any number of clients increment and decrement concurrently, offline too, without losing a step — each writer keeps its own tally and the value is the sum — bound with `useCrdtCounter({ cell, remote, push })` (`value`, `add(n)`) and read with `crdtCounterValue(state)`.
|
|
669
|
+
- **CRDT cells keep a version history that can be restored** — `@voltro/runtime`, `@voltro/cli`, `@voltro/local-first`
|
|
670
|
+
|
|
671
|
+
The new framework table `_voltro_crdt_versions` keeps a `crdtText()` / `crdtDoc()` / `crdtCounter()` cell's history: named snapshots (`snapshotCrdt`), the state before every restore, and the lineage every hard compaction archived. `listCrdtVersions` lists them without their bytes, and `restoreCrdtVersion` writes a version's content back as an ordinary CRDT edit (`CrdtBackend.restoreTo`, for text, rich text, maps and arrays) that merges with concurrent edits and is itself undoable. Every function reads the cell's row through the store it is given first, so tenant scope and row filters decide whose history is visible. `crdtCellStats` reports a cell's size, epoch, author count, history length and limits.
|
|
672
|
+
- **The server bounds and validates what a client writes into a CRDT column** — `@voltro/runtime`, `@voltro/cli`, `@voltro/local-first`
|
|
673
|
+
|
|
674
|
+
A CRDT update is the client's own bytes. `crdt.maxUpdateBytes` (default 1 MiB) bounds one incoming update, `crdt.maxDocumentBytes` (default 16 MiB) the merged document after compaction, and `crdt.validate` runs the app's own check of every merged document (return a message key to refuse). Each refusal is a typed `ValidationError` with the column as `field` (`crdt.updateTooLarge`, `crdt.documentTooLarge`, `crdt.updateNotDecodable`, or the app's key); an oversized update inside a transaction is refused before commit. Bytes that are not a CRDT update are now this typed refusal instead of an untyped error. On the client, `createSyncClient` treats a `ValidationError` / `ValidationErrors` / `BusinessRuleViolation` as final (`isRefusal`): the write and the same cell's queued writes are dropped, the cell falls back to the server's state, the unsent copy is discarded, and `onRefused` / `lastRefusal()` / the hooks' `refused` report it — instead of retrying forever. Env overrides: `VOLTRO_CRDT_MAX_UPDATE_BYTES`, `VOLTRO_CRDT_MAX_DOCUMENT_BYTES`.
|
|
675
|
+
- **useLocalQuery filters and sorts the device's replica of a table, offline** — `@voltro/client`, `@voltro/testing`
|
|
676
|
+
|
|
677
|
+
`useLocalQuery(api, table, { where, orderBy, limit })` queries every row of `table` the api's cache holds — from each single-table query, from the durable mirror after an offline reload, and every queued write's preview — instantly and offline, for any filter, without a declared query. It only sees rows the server already sent this subject, so tenant scope and row filters still apply. `SubscriptionCache.rowsOf(table)` / `subscribeAny` are the reads behind it, and `runLocalQuery` is the pure query. The mirror's `warm: 'all'` keeps every local-first list warm, so the replica is the whole readable table.
|
|
678
|
+
- **Use guarded source capture for MySQL and MariaDB Timeline** — `@voltro/cli`
|
|
679
|
+
|
|
680
|
+
MySQL and MariaDB Timeline recording now uses a guarded source journal across connections and replicas instead of local change notifications. Migration setup prepares its contract, schema changes retire old history, and dev/serve hosts share the scoped recorder. Complete frames are acknowledged before publication; missing evidence, expiry or interruption starts a fresh recorder identity. Initial setup requires a suitably privileged migration identity for the fixed-schema generation helper; ordinary serving can use schema-scoped rights without global PROCESS. If optional capture preparation fails, startup warns and continues with history unavailable rather than presenting incomplete replay.
|
|
681
|
+
|
|
682
|
+
Preparation follows the configured workflow runner storage, so the first app start does not invalidate capture by creating missing runtime tables. Create-only migration and schema apply/deploy use the shared SQL-client backend selection.
|
|
683
|
+
- **useOutbox({ api }) is an offline write lane with previews that survive a reload** — `@voltro/client`, `@voltro/testing`
|
|
684
|
+
|
|
685
|
+
`useOutbox` takes `api: '<name>'`. Bound to an api, `send` defaults to that api's own mutation for the entry's tag with the entry id as the idempotency key, so a resend after a lost answer runs the mutation once; every queued write previews through the mutation's declared `targets` on every query of its table — including queries opened later and rows rendered from the mirror after a reload — until it is sent (the server's echo then replaces the preview) or conflicts (reverted); connectivity follows the api's transport; and a sign-in or tenant switch drops the previous subject's queue from memory and loads the next one's. A transiently failed write is now retried after `retryDelay(attempts)` (default `defaultOutboxRetryDelay`) rather than only on the next online edge, which never comes when the server is down and the network is up. `stamp: clock.now` records a hybrid logical clock stamp on each entry. `useApiOnline(name?)` answers whether a write can reach the server: the browser's signal and the api's transport. The HTTP drain (`drainOutboxOverHttp`, the service worker's) now sends the entry id as the idempotency key too. A `navigator` without `onLine` (React Native, Node) no longer reads as offline. The client golden churn is additive: an optional parameter and optional fields.
|
|
686
|
+
- **Expose reviewed offline SQLite decimal conversion** — `@voltro/database`
|
|
687
|
+
|
|
688
|
+
The server-only `@voltro/database/sql` entry point exposes source capture, reviewed staging, publication, rollback and cleanup operations for exact SQLite-family decimal storage, together with their input types and canonical decision identity helper. Repeated migration execution verifies retained artifacts instead of replacing later live writes.
|
|
689
|
+
|
|
690
|
+
Conversion requires stopped application readers and writers and a nontransactional before-schema file migration. It does not automatically convert existing NUMERIC columns or recover precision already lost in a NUMERIC source. The bilingual migration guide documents that boundary and the repeatable review-input contract.
|
|
691
|
+
|
|
692
|
+
### Changed
|
|
693
|
+
|
|
694
|
+
- **CRDT sync retries on its own clock and follows the api's transport** — `@voltro/local-first`
|
|
695
|
+
|
|
696
|
+
`createSyncClient` now retries a failed write after `retryDelay(attempts)`, by default `defaultRetryDelay` (exponential from 1 s, capped at 30 s, jittered), instead of five back-to-back attempts followed by a wait for the next `setOnline(true)` or edit; edits queued while the server was away and the network stayed up used to wait for the user's next keystroke. `maxAttempts` now defaults to unbounded. `useCrdtText` and `useCrdtDoc` follow connectivity by themselves — the browser's `online`/`offline` signal AND the api's own transport, so a server that is down while the network is up holds the queue too (`api: 'app'` names the api; omitted, any mounted api). A changed `online` option is applied to the live client instead of rebuilding it, so an unsent edit is no longer dropped when connectivity flips. `useCrdtDoc` now also shows unsent edits handed over after a reload in the document it renders, instead of only after the server echoed them back. Remove hand-written window listeners that only called `setOnline`, and any retry spacing inside `push`.
|
|
697
|
+
- **CRDT edits upload as increments, so large documents stay editable** — `@voltro/local-first`, `@voltro/runtime`, `@voltro/testing`
|
|
698
|
+
|
|
699
|
+
`useCrdtText`, `useCrdtDoc` and `useCrdtCounter` sent the whole local document on every push, so the upload grew with the document and one larger than `crdt.maxUpdateBytes` (default 1 MiB) could no longer be edited at all. The sync queue still holds whole local states, so offline edits coalesce as before, but a push now sends only their increment over the last state the server delivered — on Yjs and on Loro. An increment over a state the cell no longer holds, for example after a database was restored from an older backup, is refused as `crdt.updateNotDecodable` instead of being stored as changes no reader can see, and the client then sends its whole state. `SyncClient.getRemoteState(cell)` returns the last delivered state; a custom `CrdtBackend` whose `merge` keeps changes with missing dependencies (as Yjs does) implements the optional `missingDependencies(state)`, which the conformance suite checks.
|
|
700
|
+
|
|
701
|
+
### Fixed
|
|
702
|
+
|
|
703
|
+
- **Accept final agentic flow results on strict structured-output providers** — `@voltro/plugin-ai-flows`
|
|
704
|
+
|
|
705
|
+
Agentic flow planners now use the same closed structured-output envelope as structured tools for their final result. The engine returns and stores the original deliverable object, and refuses malformed or non-object results.
|
|
706
|
+
- **Commit release state and audit events atomically** — `@voltro/plugin-releases`
|
|
707
|
+
|
|
708
|
+
Release management mutations now share one store transaction across their state, channel pointers, retention changes and audit event in both RPC and Inspect. A failed event write no longer leaves a partially applied rollout or rollback. Inspect reports internal persistence failures as HTTP 500 rather than invalid input.
|
|
709
|
+
|
|
710
|
+
Plugin mutation executors receive the optional request transaction as `PluginRouteContext.mutationStore`. Releases reuses it so later caller failures also roll back release writes; Inspect and direct calls open their own transaction.
|
|
711
|
+
- **Roll back failed SQLite-family table rewrites atomically** — `@voltro/database`, `@voltro/sql-turso`
|
|
712
|
+
|
|
713
|
+
SQLite-family table rewrites now keep copying, replacement and index recreation inside one atomic operation. Failed statements and invalid foreign keys roll back the replacement without losing the original table or cascading child rows. Each rewrite creates a fresh working table and records its atomic execution mode before DDL, preserving unrelated staging-shaped tables on both first execution and retry. Native Turso additionally checks relationships before committing; libSQL uses its atomic migration batch through both direct and supervised worker transports. Rewrites reject enclosing transactions before changing data, because foreign-key settings must be changed before their own transaction starts. Whole migration plans still use the existing resume ledger; this does not enable encoded decimal storage or perform automatic decimal conversion.
|
|
714
|
+
|
|
715
|
+
Atomic rewrites prepare statements afresh, so a retry after resolving a schema conflict does not reuse a cached preparation failure from the rolled-back attempt.
|
|
716
|
+
- **Share binlog CDC control queries and report both owned connections** — `@voltro/sql-mysql`, `@voltro/cli`
|
|
717
|
+
|
|
718
|
+
MySQL and MariaDB CDC share one control connection for metadata, persisted-offset validation and watchdog probes instead of retaining an additional SQL pool. Together with the replication stream, CDC owns two connections outside the primary pool; the boot report now counts both. Recovery awaits the old reader and control connection before replacement, and shutdown cancels retry backoff and releases owned connections. Custom binlog-position resolvers remain supported.
|
|
719
|
+
- **Keep binlog reconnects within their connection budget** — `@voltro/sql-mysql`
|
|
720
|
+
|
|
721
|
+
MySQL and MariaDB CDC reconnects terminate the previous replication thread on the server before attaching its replacement, including after the control connection is lost. Shutdown and reconnect cleanup are serialized, preventing overlapping readers from exceeding the two-connection CDC budget.
|
|
722
|
+
- **SQL readiness answers when connection acquisition stalls** — `@voltro/cli`
|
|
723
|
+
|
|
724
|
+
Bound the SQL readiness ping to ten seconds even when the database driver leaves pool acquisition unbounded. Concurrent and repeated probes share one outstanding ping, return HTTP 503 on timeout, and recover after the pool serves queries again.
|
|
725
|
+
- **Browser loader integration retains callable client declarations** — `@voltro/client`
|
|
726
|
+
|
|
727
|
+
The published client query entry retains its TypeScript declaration for browser loader integration. Application loaders continue to read through `ctx.query`.
|
|
728
|
+
- **Retain browser-loader query declarations** — `@voltro/client`, `@voltro/web`
|
|
729
|
+
|
|
730
|
+
The `@voltro/client/query` entry and the existing Client and Web root exports now retain the browser loader helper in their shipped declarations and API reports. Its release annotation previously removed the exported helper during declaration rollup.
|
|
731
|
+
- **Changelog package filters include every affected package**
|
|
732
|
+
|
|
733
|
+
Release notes retain all affected package labels, including entries with multiple packages. Package filters on the public changelog include these changes consistently.
|
|
734
|
+
- **New string columns respect the store charset before table conversion** — `@voltro/database`
|
|
735
|
+
|
|
736
|
+
MySQL and MariaDB migration plans stamp the declared character set on newly added string columns and backfill enforcement, including columns added before the same table's conversion. A mixed add-and-convert plan converges in one apply.
|
|
737
|
+
- **Foreign-key operations wait for table creation and charset conversion** — `@voltro/database`
|
|
738
|
+
|
|
739
|
+
New foreign-key tables and their descendants wait for their parent's charset conversion even when the same plan changes its columns. Adding a foreign key waits for both endpoints to exist in the target charset, including constraints deferred from cyclic table creation.
|
|
740
|
+
- **Compose baselines support workspaces without a web app** — `@voltro/cli`
|
|
741
|
+
|
|
742
|
+
Generated development and production Compose files omit the web reverse proxy when no web app is declared. API-only workspaces now pass Compose validation instead of failing on an empty reverse-proxy dependency list; each API keeps its own database and sibling API addresses.
|
|
743
|
+
- **Compose dependency volumes remain distinct for every package path** — `@voltro/cli`
|
|
744
|
+
|
|
745
|
+
The `compose` baseline validates discovered workspace package paths before replacing generated files and assigns bounded, deterministic dependency-volume names with a path hash. Distinct valid paths containing dots, underscores or dashes no longer collide. Run `voltro baseline sync` after upgrading; the next startup uses fresh dependency volumes and leaves former dependency volumes unused. Preserve database volumes when cleaning up; `docker compose down -v` would also remove database data.
|
|
746
|
+
- **Compose baselines refuse malformed apps and generated-variable collisions** — `@voltro/cli`
|
|
747
|
+
|
|
748
|
+
`voltro baseline sync` validates app manifest structures before writing generated files and refuses baseline-owned environment names in both `productionSecrets` and `optionalProductionEnv`, including database settings, runtime settings, web origins and sibling API addresses. Invalid declarations now name the manifest field to correct instead of silently omitting an app or producing duplicate YAML keys.
|
|
749
|
+
- **Compose prepares workspace dependencies before development startup** — `@voltro/cli`
|
|
750
|
+
|
|
751
|
+
The `compose` development baseline now runs a frozen install and builds the transitive workspace dependencies of every generated API/web app before migration or app startup. Shared dependencies build once in dependency order; packages without a build script are skipped, while generated apps and the workspace root are excluded. Preparation failures block startup. After upgrading, run `voltro baseline sync`; remove dependency-build replacements from custom overlays while retaining unrelated application settings. Preparation runs at startup and does not watch library sources. The `compose-mariadb` template gains the same preparation barrier for its default app paths; review and back up existing service customizations before reapplying it with `voltro baseline set compose-mariadb`, because its sync command only refreshes Dockerfiles. Both Compose production templates skip source-only dependencies and exclude the root aggregate build.
|
|
752
|
+
- **Resolve content loaders in compiled server bundles** — `@voltro/cli`, `@voltro/content`
|
|
753
|
+
|
|
754
|
+
Compiled web server bundles now include the lazy content rendering pipeline rather than retaining a relative import to an absent server loader. Published content packages use a Node-resolvable JavaScript filename. Browser builds continue to exclude the filesystem, markdown renderer and highlighting pipeline.
|
|
755
|
+
- **CRDT folds stay lossless across replicas** — `@voltro/runtime`, `@voltro/database`
|
|
756
|
+
|
|
757
|
+
A `crdtText()` / `crdtDoc()` write is read, merged and written back. Writers in one process were serialised, but two replicas folding the same row could both merge into the same base, and the later write could drop the earlier edit when the row stayed contended. The write is now a compare-and-swap: it only lands if the stored state is still the one it merged into, otherwise it re-reads and re-merges, and a fold that keeps losing is refused (the client's sync queue retries it) rather than written over another replica's edit. The in-memory store now compares bytes by content in predicates, as SQL does.
|
|
758
|
+
- **Keep CRDT inspection bounded and recover from invalid reads** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
759
|
+
|
|
760
|
+
Validate complete CRDT inspection responses before rendering so malformed nested facts show a recoverable error instead of crashing the dashboard. Count document versions in the store instead of loading every history ID for each displayed document.
|
|
761
|
+
- **Keep CTE references local in tenant namespace queries** — `@voltro/database`
|
|
762
|
+
|
|
763
|
+
SELECT compilation now carries lexical CTE names through joins, compound branches and membership/existence subqueries. Only physical tables receive the tenant namespace qualifier, so a CTE cannot accidentally resolve to a similarly named tenant table. Compound queries also retain their own WITH declarations instead of discarding them.
|
|
764
|
+
- **Refresh dashboard app reachability independently** — `@voltro/cli`
|
|
765
|
+
|
|
766
|
+
Probe discovered and registered dashboard apps independently and refresh their reachability automatically. Preserve healthy peer observations when the registry changes, cancel outdated requests on credential changes or navigation, and label unknown app types without counting them as web apps.
|
|
767
|
+
- **Recover independent app overview observations** — `@voltro/cli`
|
|
768
|
+
|
|
769
|
+
Recover individual app overview observations independently without hiding healthy routes or discarding RPC drafts during metadata outages. Cancel obsolete reads when app credentials change, reject invalid invocation snapshots, and deduplicate live-event replay per process. Show unavailable latency explicitly when counters have no duration measurements.
|
|
770
|
+
|
|
771
|
+
Use the shared labelled event-channel selector with empty-filter recovery, keyboard-expandable payloads and mobile route tabs. Label the actual invocation observation window and remove misleading growth indicators from static inventory counts.
|
|
772
|
+
|
|
773
|
+
Distinguish missing applications from initial discovery and registry failures, with a persistent return link and automatic recovery. Abort shared discovery reads when the last subscriber leaves and reject their late replies.
|
|
774
|
+
|
|
775
|
+
Include SPA in route filters, localize remaining metadata/table labels and make route tooltips and table scroll areas keyboard-accessible.
|
|
776
|
+
- **Preserve RPC drafts during temporary definition read failures** — `@voltro/devtools-ui`
|
|
777
|
+
|
|
778
|
+
Preserve open RPC execution drafts and pending results across temporary definition-read failures while hiding stale controls; inspection-context changes still reset the editor and recovery never repeats an invocation.
|
|
779
|
+
- **Shared table updates observe every known replica** — `@voltro/cli`
|
|
780
|
+
|
|
781
|
+
The local dashboard combines shared-store change feeds from all known app replicas, so writes through another replica update the visible table promptly instead of waiting for the periodic snapshot refresh. Row reads still honor the selected responder and writes execute once. Fleet membership changes update the feeds; missing inventory or disconnected members remain visible as reconnecting.
|
|
782
|
+
- **Dashboard live streams recover through known fleet entry points** — `@voltro/cli`
|
|
783
|
+
|
|
784
|
+
Local dashboard live streams now reuse the authorized fleet routes learned by ordinary reads. When an entry point stops, change notifications can reconnect through a remaining replica instead of waiting for periodic snapshots. Explicit replica selections, credential isolation and target checks remain enforced; unavailable selected processes stay unavailable and failed writes are not replayed.
|
|
785
|
+
- **Unconfirmed data cell saves show recovery context** — `@voltro/devtools-ui`
|
|
786
|
+
|
|
787
|
+
Failed inline saves explain that the change may already be stored, retain the draft, and request a fresh row snapshot. The last observed value and expandable error details help review the outcome. Explicit retries preserve the original compare-and-set expectation.
|
|
788
|
+
|
|
789
|
+
Editor controls wrap within narrow columns instead of being clipped by a fixed editor minimum width.
|
|
790
|
+
- **Data cell save state survives read outages** — `@voltro/devtools-ui`
|
|
791
|
+
|
|
792
|
+
Inline cell edits keep their pending-save lock when a temporary row or schema read failure hides the grid. Returning editors cannot submit a duplicate request, and save errors received while hidden reappear with the original draft.
|
|
793
|
+
- **Local data drafts survive app-metadata outages** — `@voltro/cli`
|
|
794
|
+
|
|
795
|
+
The local data explorer keeps its editor mounted when app-metadata reads fail, including a temporary outage of every API replica. The error stays visible while automatic retries restore the data view and its unfinished cell draft. Changing the app, endpoint or Inspect credentials resets the editor context. Retained routing information does not grant permissions or present failed observations as current data.
|
|
796
|
+
- **Treat database-generated columns as read-only in the data explorer** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
797
|
+
|
|
798
|
+
The shared data explorer preserves generated-column metadata, displays computed values without edit controls, and omits them from row creation. Inspect writes reject generated inputs explicitly instead of relying on silent stripping or database errors.
|
|
799
|
+
- **Inline data drafts survive temporary read failures** — `@voltro/devtools-ui`
|
|
800
|
+
|
|
801
|
+
The data explorer retains an inline cell's draft when a temporary row or table metadata failure rebuilds the grid. Recovery preserves unfinished input without submitting it or resetting it to the database value. Saving still compares against the original value, so external changes remain protected by conflict detection.
|
|
802
|
+
- **Resolve database command roots without treating migration IDs as paths** — `@voltro/cli`
|
|
803
|
+
|
|
804
|
+
Database commands honor an explicit `--root` in either supported spelling. File-migration rollback resolves the app configuration and migration directory from that root, or the current directory when omitted, instead of interpreting the positional migration ID as an app path.
|
|
805
|
+
- **Match decimal relation keys across column scales** — `@voltro/database`
|
|
806
|
+
|
|
807
|
+
The batched eager loader now associates equivalent decimal keys even when related columns return different scales. One, many and junction relationships compare exact decimal values without converting them to floating-point numbers. Returned rows retain their original values, and text keys keep their exact string identity.
|
|
808
|
+
- **Preserve encoded decimal schema identity** — `@voltro/database`
|
|
809
|
+
|
|
810
|
+
Preserve internal SQLite-family decimal encoding, precision and scale through schema introspection and snapshot reconstruction. Block representation or encoded-shape changes that require explicit data conversion instead of treating them as ordinary type casts. Ordinary decimal declarations still use their existing storage.
|
|
811
|
+
- **Failed deploy undo reports every completed schema reversal** — `@voltro/cli`
|
|
812
|
+
|
|
813
|
+
When undo cannot complete, `revertedOperations` retains the schema reversals already performed, including foreign-key removals and completed character-set conversions. The failing operation is excluded, so the report accurately names what was undone and what still needs repair.
|
|
814
|
+
- **Soft-drop deploy undo preserves rows without blocking parent restoration** — `@voltro/cli`
|
|
815
|
+
|
|
816
|
+
On MySQL and MariaDB with `VOLTRO_SOFT_DROP=1`, a failed deploy removes outgoing foreign keys from newly created app tables before retaining them as snapshots, allowing active parent tables to regain their original character sets. The undo verifies baseline columns separately from newly retained snapshot columns and removes newly created framework tables before charset restoration.
|
|
817
|
+
- **Reduce repeated binding resolution in Doctor source scans** — `@voltro/cli`
|
|
818
|
+
|
|
819
|
+
Doctor indexes identifier names per source-file AST and skips form binding resolution when a submit path is absent. Declaration identity, shadowed bindings, complete file lists, and JSON findings remain unchanged.
|
|
820
|
+
- **Reduce overhead for immediate event publication** — `@voltro/runtime`
|
|
821
|
+
|
|
822
|
+
Event publication without a transaction or durable audience avoids an extra Effect generator. Validation and delivery remain lazy until execution; encoded size limits, transaction ordering, durable acknowledgements and both await and yield styles are preserved.
|
|
823
|
+
- **Gateway BYOK costs follow nested routing receipts** — `@voltro/ai`
|
|
824
|
+
|
|
825
|
+
`gatewayCostUsd` reads the final successful provider attempt of the final successful model attempt from `gateway.routing.modelAttempts[].providerAttempts`, so BYOK calls include the reported `marketCost` and gateway-credit fallbacks keep the gateway charge.
|
|
826
|
+
- **Keep global logs stable while pausing and reconnecting** — `@voltro/cli`
|
|
827
|
+
|
|
828
|
+
The local dashboard's global Logs page pauses the display while continuing to receive a bounded buffer. Resuming shows the latest captured entries without reopening streams. Replay identities prevent duplicate records after reconnects, and clearing rows preserves those identities so cleared records stay cleared. Unchanged app subscriptions remain open when another app appears. Connection failures retain captured logs and show automatic recovery status.
|
|
829
|
+
- **Isolate global metrics observations and recover failed sources** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
830
|
+
|
|
831
|
+
Keep global dashboard invocation metrics responsive when another app is slow or newly discovered. Poll each app independently, preserve unchanged observations, and abort requests on app, credential, or navigation changes so late replies cannot replace the current source.
|
|
832
|
+
|
|
833
|
+
Reuse the shared invocation snapshot validator in the global page, reject malformed observations per source, preserve sort focus, and keep mobile tables keyboard-accessible.
|
|
834
|
+
- **Keep inspect creation receipts in their own deployment** — `@voltro/data-transfer`
|
|
835
|
+
|
|
836
|
+
Default logical exports omit inspect creation receipts, and logical imports preserve the destination's own receipts. A creation accepted elsewhere must not suppress or replay a command in the destination. Native disaster-recovery backups retain their existing behavior.
|
|
837
|
+
- **Count inspected rows without loading every matching ID** — `@voltro/cli`
|
|
838
|
+
|
|
839
|
+
Calculate Data explorer totals with a filtered store-side COUNT instead of loading every matching ID. Pagination still reports the complete filtered total, and requests without a count skip the extra query. Invalid count results fail explicitly instead of producing a misleading total.
|
|
840
|
+
|
|
841
|
+
Wire read-only creation-outcome observations in production startup as well, preserving receipt visibility without enabling arbitrary production data writes.
|
|
842
|
+
- **Loro apps run under voltro dev, and the collab template can switch to Loro** — `@voltro/cli`
|
|
843
|
+
|
|
844
|
+
Under `voltro dev` the browser resolved `loro-crdt` to its `bundler` build, which imports its WebAssembly as an ES module Vite cannot load, so every page using the Loro backend failed with "Cannot read properties of undefined (reading 'memory')"; `voltro build` was unaffected. `voltro dev` and `voltro build` now resolve every browser-side import of `loro-crdt` — the app's, `@voltro/local-first/loro`'s and `loro-prosemirror`'s — to its `browser` build, so there is one Loro instance, while server-side code keeps the Node build. The `api-collab` and `frontend-collab` templates choose their CRDT backend in one `lib/crdt.ts` file per app, and their README shows the switch to Loro.
|
|
845
|
+
- **Exact bigint sums and extrema in the memory store** — `@voltro/runtime`
|
|
846
|
+
|
|
847
|
+
The memory store now sums declared `bigint` values without losing decimal-string precision, includes them in floating-point averages, and compares their minimum and maximum numerically. Storage statistics retain their byte totals when metadata uses `bigint`, including inside transactions.
|
|
848
|
+
- **Install SQL Server driver dependencies in standalone workspaces** — `@voltro/cli`
|
|
849
|
+
|
|
850
|
+
`voltro add mssql` declares a missing SQL Server driver at the running CLI's lockstep version so an ordinary app can install it without local framework packages. Existing driver ranges are preserved. The completion message describes both shipped SQL Server patches.
|
|
851
|
+
- **Stop reporting SQL Server CDC retries after reader shutdown** — `@voltro/sql-mssql`
|
|
852
|
+
|
|
853
|
+
A stopped Change Tracking reader no longer reports a late in-flight poll failure as a retry or calls its error consumer. Active reader failures continue to be reported.
|
|
854
|
+
- **SQL Server deploy prepares workflow storage before serving** — `@voltro/cli`
|
|
855
|
+
|
|
856
|
+
SQL Server schema apply and deploy now prepare runtime-owned workflow storage before Timeline capture activation, allowing an already provisioned app to start with automatic migrations disabled and a serving identity without schema DDL rights. Timeline cleanup waits for in-flight SQL before rollback, preventing interrupted observers from leaving leases behind during shutdown.
|
|
857
|
+
- **Preserve exact SQL Server decimal row values** — `@voltro/sql-mssql`, `@voltro/database`
|
|
858
|
+
|
|
859
|
+
Preserve exact DECIMAL and NUMERIC strings in SQL Server mutation results and bulk change-event row images. Conversion occurs only when returning captured write results, retaining numeric storage and SQL arithmetic, NULL values, trigger compatibility, and MERGE action metadata.
|
|
860
|
+
|
|
861
|
+
Change Tracking reads use the same lossless final projection, including decimal columns added while the reader is running. Typed row SELECT projections also preserve declared decimal fields, including aliases and DISTINCT pagination, while filtering, ordering and nested SQL calculations remain numeric. Raw SQL, direct aggregate results, set operations and eager JSON projections remain outside this conversion boundary.
|
|
862
|
+
- **Await SQL Server cancellation before pooled connection reuse** — `@voltro/sql-mssql`, `@voltro/cli`
|
|
863
|
+
|
|
864
|
+
Interrupted SQL Server queries and procedures cancel their request and await its completion before pooled reuse or transaction rollback. Normal SQL failures still propagate, and cancellation does not wait for the query's full duration. The CLI ships the driver patch alongside the cluster patch. Existing SQL Server projects must rerun `voltro add mssql` and `pnpm install` at their pnpm workspace root to receive it; `voltro update` includes these migration steps.
|
|
865
|
+
- **Use native SQL Server renames for retained schema snapshots** — `@voltro/database`
|
|
866
|
+
|
|
867
|
+
SQL Server soft-drop now reuses the native table and column rename paths instead of emitting unsupported ALTER TABLE RENAME syntax. Checked columns retain their rewritten constraints and state. Recorded snapshots restore original table/column names and retained values through the existing restoration path.
|
|
868
|
+
- **Retire SQL Server Timeline capture within atomic schema deployments** — `@voltro/cli`
|
|
869
|
+
|
|
870
|
+
SQL Server Timeline retirement now joins an existing atomic schema-migration transaction, so real schema changes and soft-drop snapshots no longer fail the independent read-frame guard. Rollback restores capture ownership, triggers and application schema together. Serving replay frames continue to require a fresh transaction.
|
|
871
|
+
- **Reject orphan Timeline capture before SQL Server migrations** — `@voltro/cli`
|
|
872
|
+
|
|
873
|
+
SQL Server migration preparation detects leftover Timeline capture triggers even when private capture tables are missing and the serving schema resolves application tables through dbo fallback. Inconsistent ownership blocks application DDL; unrelated triggers on shadowed tables remain untouched.
|
|
874
|
+
- **SQL Server writes work with Timeline capture triggers** — `@voltro/sql-mssql`
|
|
875
|
+
|
|
876
|
+
SQL Server writes now capture returned row images with OUTPUT INTO, preserving normal writes, generated values, bulk rollback and MERGE upserts when Timeline or other DML triggers are enabled.
|
|
877
|
+
- **Read MySQL bulk inserts back through explicit primary keys** — `@voltro/sql-mysql`
|
|
878
|
+
|
|
879
|
+
MySQL `insertMany` now uses declared named and composite primary keys instead of assuming that a missing `id` means AUTO_INCREMENT. Batched readback preserves input order and database defaults, including keys whose database formatting differs from the input. All insert chunks and their readback share one transaction, and change events are emitted only after the complete operation succeeds. Missing explicit key components are rejected before writing. Ordinary `id` recovery and MariaDB's native RETURNING path retain their existing behavior.
|
|
880
|
+
- **Recover single MySQL inserts by their declared primary key** — `@voltro/sql-mysql`
|
|
881
|
+
|
|
882
|
+
Single MySQL inserts now share explicit-key readback with bulk inserts. A previous AUTO_INCREMENT operation on the connection can no longer select a stale `id`, and an ordinary non-key `id` column cannot cause the wrong row to be returned. Named and composite keys identify the persisted row independently of nullable non-key values; database defaults and namespace isolation are preserved. Ordinary AUTO_INCREMENT recovery and MariaDB RETURNING retain their existing behavior.
|
|
883
|
+
- **An empty agent inventory disables planner delegation** — `@voltro/plugin-ai-flows`
|
|
884
|
+
|
|
885
|
+
An explicit empty `EngineDeps.listAgents` result removes `run_agent` from planner tools. Failed listing refuses planning without widening allowed refs, and reserved tool names remain protected. An absent callback retains the plan-ref fallback.
|
|
886
|
+
- **Keep workspace Radix hooks on one React instance during prerender** — `@voltro/cli`
|
|
887
|
+
|
|
888
|
+
The build-time prerender and source middleware loaders now resolve Radix primitives through the same React deduplication as development. Building a dashboard that imports UI primitives from another workspace installation no longer fails with a null React hook dispatcher during static rendering.
|
|
889
|
+
- **Distinguish uncertain record saves and deletions** — `@voltro/devtools-ui`
|
|
890
|
+
|
|
891
|
+
Record dialogs now explain when a save or deletion was not confirmed, retain the draft and original compare-and-set expectations, and refresh the table observation after an uncertain response. Closing the dialog does not undo a committed write. Retries continue to reject intervening changes instead of adopting refreshed values as their expectation.
|
|
892
|
+
|
|
893
|
+
Late record and bulk-delete completions no longer close a newer dialog or clear a newer selection after a view change. Bulk deletion now explains unconfirmed outcomes and preserves original retry expectations. A successful batch retires only the submitted selection.
|
|
894
|
+
- **Reject mismatched decimal storage in typed readers** — `@voltro/database`
|
|
895
|
+
|
|
896
|
+
SQLite-family typed decimal reads now reject physical encoded keys when the application schema does not describe that representation, rather than expose those keys as decimal values. Explicitly encoded row decoding and logical query results retain their existing behavior. This check does not replace coordinated reader/writer exclusion during a storage migration or activate encoded storage.
|
|
897
|
+
- **Recheck observed release channel state before confirmation** — `@voltro/devtools-ui`
|
|
898
|
+
|
|
899
|
+
An open rollout, rollback or policy confirmation now becomes unavailable when the dashboard observes a changed channel state. The draft remains visible with an explanation; cancel and reopen the action to review the current target. Identical polling snapshots keep confirmations usable. This UI check does not provide server-side compare-and-swap semantics.
|
|
900
|
+
- **Validate release forms and isolate pending target actions** — `@voltro/devtools-ui`
|
|
901
|
+
|
|
902
|
+
The shared Releases dashboard rejects empty percentages and values outside 0–100 before submitting a rollout. Empty input is no longer converted to zero. An inline message explains the permitted range; explicit whole values, including zero and 100 remain supported. Pending actions lock their draft and prevent rapid duplicate submissions. A failed request preserves the draft for review and an explicit retry.
|
|
903
|
+
|
|
904
|
+
The canonical promote/rollout input schemas now reject fractional percentages before storage, matching the integer rollout column. Inspect reports HTTP 400 instead of a PostgreSQL persistence error for these invalid inputs.
|
|
905
|
+
- **Validate release inspect actions before execution** — `@voltro/plugin-releases`
|
|
906
|
+
|
|
907
|
+
Release inspect write endpoints now decode inputs through the corresponding RPC descriptor schemas. Malformed field types return HTTP 400 before changing release state or writing audit events, instead of reaching the executor unchecked.
|
|
908
|
+
- **Rendered-page styles, font caching and accessible code content** — `@voltro/cli`, `@voltro/content`, `@voltro/ui-shadcn`
|
|
909
|
+
|
|
910
|
+
Rendered pages include their static CSS dependencies before hydration, including locale variants and SSR/ISR shells. Fingerprinted generated fonts receive immutable caching. Markdown heading permalinks remain keyboard-accessible with meaningful names, and offscreen code blocks defer optional highlighting with corrected light-theme syntax contrast.
|
|
911
|
+
|
|
912
|
+
Islands entry shells also retain generated font definitions and preloads, so SSR sign-in pages use the configured fonts before hydration.
|
|
913
|
+
|
|
914
|
+
Production SSR streams negotiate Brotli/Gzip with per-chunk flushes, preserving initial-shell and deferred-boundary delivery without buffering the complete document.
|
|
915
|
+
|
|
916
|
+
Shared code CSS preserves light-theme inline colors alongside variable-based output, with dark-theme overrides retained.
|
|
917
|
+
- **Wake queued schedules when completion arrives during a turn check** — `@voltro/runtime`
|
|
918
|
+
|
|
919
|
+
Queued schedules retain run-ledger notifications received while reading a blocker and immediately re-check before waiting. A completion arriving at that boundary no longer adds a full polling interval; ordering, conditional turn-taking and the polling fallback remain enforced.
|
|
920
|
+
- **Expand integer columns without destructive migration authorization** — `@voltro/database`
|
|
921
|
+
|
|
922
|
+
Changing an `integer()` column to `bigint()` now plans a safe, value-preserving schema expansion. Existing rows and defaults are retained. SQLite and Turso already store integers in 64 bits, so this declaration change needs no DDL there. Reducing a `bigint()` column to `integer()` still requires explicit migration authorization.
|
|
923
|
+
|
|
924
|
+
SQL Server integer-range migrations preserve named defaults, CHECK trust and enabled state, and ordinary row-store index definitions inside the schema transaction. Foreign keys, schema-bound expressions, user-created statistics, and unsupported partitioned, compressed or non-row-store indexes are refused before removing any dependency and require a reviewed file migration. Explicit SQL Server `USING` transforms are refused instead of being silently ignored; PostgreSQL continues to apply the declared expression.
|
|
925
|
+
- **Require patched image processing dependencies** — `@voltro/cli`, `@voltro/plugin-storage`
|
|
926
|
+
|
|
927
|
+
Require sharp 0.35.5 or later for image processing to resolve the high-severity librsvg dependency advisory GHSA-wq5f-xc86-pv6w.
|
|
928
|
+
- **Scope SQL callback upserts to their complete conflict keys** — `@voltro/sql-postgres`, `@voltro/sql-mysql`, `@voltro/sql-mssql`
|
|
929
|
+
|
|
930
|
+
Postgres, MySQL, MariaDB and SQL Server now update the existing row through all declared conflict columns instead of assuming a text `id`. This fixes explicit and composite keys and prevents numeric IDs from producing an unfiltered update. The same key handling covers MySQL/MariaDB partial-insert fallbacks and SQL Server recovery when another writer wins an insert race. Existing transaction and concurrency contracts are unchanged.
|
|
931
|
+
- **SQL Server accepts Uint8Array binary parameters without conversion errors** — `@voltro/sql-mssql`
|
|
932
|
+
|
|
933
|
+
SQL Server now binds plain `Uint8Array` values as binary parameters, including subarrays, empty bytes, and payloads larger than 8,000 bytes. The adapter preserves each view's offset and length, retains existing `Buffer` support, and applies the same conversion to store writes and raw SQL instead of rejecting valid byte values with `EPARAM`.
|
|
934
|
+
- **Connect SQL Server Timeline to shared capture hosts and migrations** — `@voltro/cli`
|
|
935
|
+
|
|
936
|
+
Select coherent SQL Server source capture at the shared Timeline host boundary. Schema preparation installs its persisted capture contract; schema maintenance retires it before application DDL. Serving reads existing activation and can recover after later provisioning without installing schema objects itself.
|
|
937
|
+
|
|
938
|
+
Missing database Change Tracking or capture permissions leave Timeline unavailable while optional preparation reports a warning and application startup continues. Document the prerequisites, namespace resolution and bounded replay contract in English and German and regenerate shipped agent guidance.
|
|
939
|
+
- **Reuse prepared SQL Server Timeline contracts without schema DDL rights** — `@voltro/cli`
|
|
940
|
+
|
|
941
|
+
Validate an existing ready capture contract before entering migration-side ownership checks. An unchanged contract can be reused with the serving account's object permissions, without schema-wide VIEW DEFINITION or DDL rights. Changed or unavailable contracts still require the migration identity for repair.
|
|
942
|
+
- **Scope SQLite callback upserts to their conflict keys** — `@voltro/sql-sqlite`
|
|
943
|
+
|
|
944
|
+
SQLite-family callback upserts now address the existing row through every declared conflict column rather than assuming a text `id`. This supports explicit and composite keys and prevents numeric IDs from producing an unfiltered update. Key values continue through the registered column codecs, including decimals.
|
|
945
|
+
- **Execute recursive tree queries on SQLite and libSQL** — `@voltro/database`
|
|
946
|
+
|
|
947
|
+
Recursive SQLite compound terms now reference the recursive relation directly instead of wrapping it in a derived table that the engine rejects as circular. Nonrecursive anchor pagination, compound limits and outer pagination remain supported. Recursive branch-local ordering or pagination fails explicitly instead of changing meaning. The bilingual guide now selects the CTE result and identifies native Turso's unsupported recursive capability.
|
|
948
|
+
- **Match SQLite write conditions and keys to their column codecs** — `@voltro/database`, `@voltro/sql-sqlite`
|
|
949
|
+
|
|
950
|
+
Encode SQLite-family row keys and conflict lookup values consistently with inserts, including timestamp keys used by updates, JSON patches, deletes and insert-ignore. Schema-aware bulk predicates also support internally marked exact decimal storage without changing ordinary decimal schema emission.
|
|
951
|
+
- **SQLite set operations preserve branch queries and pagination** — `@voltro/database`
|
|
952
|
+
|
|
953
|
+
SQLite-family UNION, UNION ALL, INTERSECT and EXCEPT now compile executable compound queries. Input filters, ordering and pagination remain scoped to each branch, including nested operations. An outer offset without a limit also compiles correctly.
|
|
954
|
+
- **SQLite replica rebalancing keeps database traffic responsive** — `@voltro/workflow`
|
|
955
|
+
|
|
956
|
+
SQLite-backed workflow runners now admit individual shard releases one at a time, avoiding a burst of lock-row deletes when replicas join or restart. This prevents shard-release fan-out from delaying dashboard live refreshes and application writes over remote libSQL. Waiting releases remain cancellable, and release failures do not block later releases.
|
|
957
|
+
- **Answer missing enumerated static paths with 404** — `@voltro/cli`
|
|
958
|
+
|
|
959
|
+
Production web serving now returns the scoped not-found page with HTTP 404 when a static route exports `getStaticPaths` but the requested document was not generated. Published paths still serve their built HTML, and dynamic static routes without enumeration retain client-side fallback.
|
|
960
|
+
- **Storage metadata supports files and tenant usage beyond 2 GiB** — `@voltro/plugin-storage`
|
|
961
|
+
|
|
962
|
+
Storage reference sizes, database blob sizes, tenant byte/count counters, and shared-object reservation counters now use 64-bit SQL integers. Existing framework tables widen through schema reconciliation while the storage API retains exact JavaScript numbers up to `Number.MAX_SAFE_INTEGER`; reservations beyond that ceiling are rejected without consuming quota. Metadata failures retain the operation phase, safe database error codes, and retry classification without exposing SQL or row contents, so numeric overflow can be distinguished from a recoverable provider acknowledgement loss.
|
|
963
|
+
- **Explain storage encoding changes in rollout guidance** — `@voltro/database`
|
|
964
|
+
|
|
965
|
+
Clarify that a storage change with an unchanged logical column type can change read semantics, not only column bounds. Rollout guidance now distinguishes parameter changes from encoding changes and requires verified conversion with compatible readers/writers or a deployment without overlap.
|
|
966
|
+
- **Storage statistics preserve tenant and visibility groups on SQL stores** — `@voltro/plugin-storage`
|
|
967
|
+
|
|
968
|
+
Storage inventory statistics now retain tenant and visibility keys alongside SQL aggregates, so each tenant receives its own byte/count totals and public/private counts remain accurate. Totals that exceed the exact JavaScript number range fail with a typed `StorageError` instead of reporting rounded values.
|
|
969
|
+
- **Preserve native timestamp precision in predicate bindings** — `@voltro/database`
|
|
970
|
+
|
|
971
|
+
Temporal predicate strings with more than three fractional digits stay exact during SQL binding instead of passing through a millisecond Date. Guarded migration rewrites can compare unchanged imported timestamps without false contention and still reject competing sub-millisecond changes. Ordinary epoch-millisecond and ISO instant coercion remains unchanged.
|
|
972
|
+
- **Release native libSQL locks after failed statements** — `@voltro/sql-turso`
|
|
973
|
+
|
|
974
|
+
The libSQL backend now receives a temporary stable-driver patch through its SDK dependency. Failed native statements release their locks without reopening the connection or discarding session state. Application imports and configuration remain unchanged, including embedded replicas; no consumer override is required.
|
|
975
|
+
|
|
976
|
+
The SDK implementation is unchanged. Its package metadata selects the patched native driver, tested across all nine upstream platform targets. This temporary distribution will be removed once an official compatible release passes the SDK, embedded-replica, lock-recovery and clean-installation checks. The separate local Turso Rust engine is unaffected.
|
|
977
|
+
- **Keep decimal parity checks aligned with the migrated schema** — `@voltro/testing`
|
|
978
|
+
|
|
979
|
+
Keep the dialect parity schema-evolution fixture's registered descriptors in sync with each accepted migration, so typed decimal writes use the correct physical encoding. Verify that SQLite-family precision changes require explicit conversion, preserve the existing schema and exact values, and continue checking later enum, backfill and constraint migrations.
|
|
980
|
+
- **Carry the declared store charset into Timeline cluster preparation** — `@voltro/cli`
|
|
981
|
+
|
|
982
|
+
Timeline preparation during schema apply, create-only migration and development boot now passes the store's declared MySQL-family charset to the shared cluster migration gate. Newly created workflow storage uses that charset without changing the database default or rewriting existing storage during serving boot.
|
|
983
|
+
- **Keep Timeline data visible on narrow screens** — `@voltro/devtools-ui`
|
|
984
|
+
|
|
985
|
+
Timeline uses a compact table selector on narrow screens and keeps sequence navigation together above the data. Large table names no longer wrap into multiple rows of buttons on mobile; desktop table buttons and full event details remain available.
|
|
986
|
+
- **Keep native Timeline restores inside their target database** — `@voltro/cli`
|
|
987
|
+
|
|
988
|
+
MySQL/MariaDB Timeline triggers restore into the selected target without retaining source-schema references. Migration phases verify their connection context before changing objects, and rebuilding copied generation intents retires only matching target helpers while preserving the source contract.
|
|
989
|
+
- **Timeline explains protected values and adapts event colors to the theme** — `@voltro/devtools-ui`
|
|
990
|
+
|
|
991
|
+
Timeline cells explain server-only, encrypted and sensitive masking in English and German instead of displaying internal marker objects. Operation badges use readable text colors in light and dark themes.
|
|
992
|
+
|
|
993
|
+
Table selection and the scrubber share a compact control area so reconstructed rows appear sooner on narrow screens. Long table lists stay scrollable and selected table controls expose their state to assistive technology.
|
|
994
|
+
- **Share native Turso namespace attachments across pool connections** — `@voltro/sql-turso`
|
|
995
|
+
|
|
996
|
+
Keep native Turso tenant attachments consistent across the connection pool. Store and migration ATTACH statements now publish the engine-resolved file mapping; stale and newly constructed connections synchronize before subsequent queries. DETACH and reattachment invalidate cached statements. Failed provisioning preserves existing mappings, and private in-memory attachments are rejected on multi-connection pools instead of silently splitting tenant data across independent databases.
|
|
997
|
+
- **Enable supported generated columns on Turso backends** — `@voltro/database`, `@voltro/sql-turso`
|
|
998
|
+
|
|
999
|
+
Native Turso now enables VIRTUAL generated columns with MVCC. The schema emitter no longer rejects generated columns for every Turso backend, allowing VIRTUAL columns locally and both STORED and VIRTUAL columns through libSQL. The native engine still rejects STORED generated columns; the requested storage mode is never silently changed.
|
|
1000
|
+
- **Manual upgrade notes include tracked SQL scripts** — `@voltro/cli`
|
|
1001
|
+
|
|
1002
|
+
Manual upgrade checks now scan tracked `.sql` files alongside source, shell scripts, configuration and runbooks. Direct database references in SQL scripts are located for review instead of silently missing an applicable upgrade note; scripts are never rewritten by these checks.
|
|
1003
|
+
- **Verify physical decimal compatibility independently of automatic migration** — `@voltro/database`, `@voltro/cli`
|
|
1004
|
+
|
|
1005
|
+
SQLite-family applications now verify physical decimal markers before accepting schema compatibility, even with a matching recorded fingerprint or automatic migration disabled. Mismatched representation, precision or scale is rejected for empty tables as well as populated ones. Development, source serve and compiled serve share the read-only check; disabling migration still prevents automatic schema changes. This does not update already-running binaries, perform conversion or activate encoded decimal storage.
|
|
1006
|
+
|
|
1007
|
+
### Internal (no consumer-facing effect)
|
|
1008
|
+
|
|
1009
|
+
- **Declare the SQLite dependency of the media replay fixture**
|
|
1010
|
+
|
|
1011
|
+
The process-restart fixture resolves its SQLite driver through an explicit test dependency instead of assuming a neighboring package has already been built. This keeps clean-checkout typechecking independent of stale local build output.
|
|
1012
|
+
- **Numeric formatting bounds excessive precision**
|
|
1013
|
+
|
|
1014
|
+
Project-owned dependency installations cap numeric formatting precision at the supported ECMAScript range, preserving normal CLI help and SQL Server diagnostic formatting. Patch integrity and installed formatter behavior are verified before a patched advisory is classified as remediated.
|
|
1015
|
+
- **Verify completed client mounting and offline transport loss** — `@voltro/cli`
|
|
1016
|
+
|
|
1017
|
+
Browser validation waits for the completed client mount and verifies offline navigation after the test origin becomes unreachable, including service-worker requests that a page's emulated offline state may leave connected. Background Sync validation observes either the queued tag or its actual worker delivery.
|
|
1018
|
+
- **Normalize generated Atom headline whitespace** — `@voltro/cli`
|
|
1019
|
+
|
|
1020
|
+
Generated release-feed headlines trim trailing whitespace after fallback-body truncation, with artifact-level regressions for XML escaping and truncation.
|
|
1021
|
+
- **Classify owned migration and statement-local DDL sites** — `@voltro/cli`
|
|
1022
|
+
|
|
1023
|
+
The derived DDL-site guard records the existing decimal migration machinery, SQL Server capture migration pipeline and statement-local tempdb output collector with explicit ownership reasons. Unknown sites and stale entries remain failures.
|
|
1024
|
+
- **Preserve CLI resource isolation during development** — `@voltro/cli`
|
|
1025
|
+
|
|
1026
|
+
Internal validation scheduling now isolates sustained CLI and database workloads. Published behavior is unchanged.
|
|
1027
|
+
- **Align codemod metadata with the complete registry** — `@voltro/cli`
|
|
1028
|
+
|
|
1029
|
+
Regenerate the package manifest from the current codemod registry so release validation includes every registered migration.
|
|
1030
|
+
- **Keep cold-start reference measurements separate** — `@voltro/cli`
|
|
1031
|
+
|
|
1032
|
+
Structural cold-start measurements can be refreshed without replacing the recorded latency measurement or its reference machine.
|
|
1033
|
+
- **Preserve collection runner diagnostics and isolate process checks** — `@voltro/cli`
|
|
1034
|
+
|
|
1035
|
+
Collection verification retains runner startup errors and configured writable caches. Checks that launch real application processes execute serially while preserving their lifecycle and connection-budget assertions.
|
|
1036
|
+
- **Acquire the Compose env audit image when missing** — `@voltro/cli`
|
|
1037
|
+
|
|
1038
|
+
The real Compose environment byte-preservation fixture pulls its declared Node image when absent instead of requiring an undocumented preloaded image. Existing cached images are reused and all actual container assertions remain unchanged.
|
|
1039
|
+
- **Keep CRDT replica test workers outside shipped-source coverage** — `@voltro/integration-harness`
|
|
1040
|
+
|
|
1041
|
+
Place the process-backed CRDT replica helper in the existing test-fixture directory and update its integration-test launcher. Coverage continues to reject newly shipped source in suite-only packages while measuring the actual library packages.
|
|
1042
|
+
- **Validate exact decimal conversion sources** — `@voltro/database`
|
|
1043
|
+
|
|
1044
|
+
Add an internal exact-decimal conversion boundary that validates shape changes without rounding and requires an explicit source-value decision for NUMERIC data that may already have lost precision. Ordinary storage and migration activation remain unchanged.
|
|
1045
|
+
|
|
1046
|
+
The internal owned conversion lifecycle now publishes a reviewed stage and can atomically restore original declared values while the published rows remain unchanged. It verifies retained artifacts, preserves later writes on completed-operation retries, and refuses stale rollback after external data changes. Explicit finalization atomically removes verified retained artifacts, closes the rollback window for published conversions, and preserves the completion receipt so retries cannot delete newly reused artifact names. Public conversion activation remains pending.
|
|
1047
|
+
|
|
1048
|
+
Retained copies and conversion stages preserve accessible implicit rowids without narrowing them through JavaScript numbers. Conversion and rollback retain AUTOINCREMENT high-water marks, including native Turso sequence allocation, and refuse rollback when row identity or allocation state has advanced.
|
|
1049
|
+
- **Preserve ungrouped aliased rows in exact decimal queries** — `@voltro/database`
|
|
1050
|
+
|
|
1051
|
+
Use the row-preserving projection pipeline for explicitly encoded decimal queries containing only aliased columns. Preserve duplicates, exact ordering, filtering, DISTINCT and pagination without requiring GROUP BY. Ordinary schema emission is unchanged; this does not enable encoded decimal storage by default.
|
|
1052
|
+
- **Route encoded decimal eager reads through schema-aware queries** — `@voltro/database`
|
|
1053
|
+
|
|
1054
|
+
Keep the SQLite JSON eager-load optimization off for explicitly encoded decimal sources, nested relation targets and junction tables. The existing batched relation loader applies exact predicates and logical row decoding through ordinary store queries. Ordinary decimal schema emission and other dialects are unchanged.
|
|
1055
|
+
- **Bound exact decimal result decoding plans** — `@voltro/database`
|
|
1056
|
+
|
|
1057
|
+
Decode explicitly encoded row and window results through parallel relational stages in their enclosing SQL scope. Reuse exact sign and shape validation, preserve physical sort keys and bound parameter order, and avoid correlated result subqueries that multiply native Turso preparation memory. Do not carry unused or duplicate window ordering keys. Ordinary decimal schema emission and storage activation remain unchanged.
|
|
1058
|
+
- **Route explicitly encoded SQLite decimal query results** — `@voltro/database`, `@voltro/sql-sqlite`, `@voltro/voltro`
|
|
1059
|
+
|
|
1060
|
+
Connect explicitly marked decimal schemas to exact SELECT lowering and projection-aware decoding, preserving ordinary NUMERIC columns, aggregate result types and namespace/CTE scope. Ordinary decimal schema emission remains unchanged pending complete storage adoption.
|
|
1061
|
+
|
|
1062
|
+
The optional query descriptor added to `decodeRowsFromSchema` preserves existing three-argument callers. Other API report changes are documentation and formatting only.
|
|
1063
|
+
- **Connect physical decimal metadata to SQLite row codecs** — `@voltro/database`, `@voltro/sql-sqlite`
|
|
1064
|
+
|
|
1065
|
+
Encode and strictly decode explicitly marked internal decimal columns at the SQLite-family row boundary. Ordinary decimal storage remains unchanged; general query and migration activation remain pending.
|
|
1066
|
+
- **Flatten exact decimal window CTE composition** — `@voltro/database`
|
|
1067
|
+
|
|
1068
|
+
Compose window calculation stages in their parent CTE scope and keep decimal window outputs physically encoded across that boundary. This avoids repeated encoding and reduces native Turso statement preparation cost. Isolate generated stage names case-insensitively and preserve bound parameter order. Ordinary decimal schema emission remains unchanged.
|
|
1069
|
+
- **Compose exact decimal queries through derived CTE sources** — `@voltro/database`
|
|
1070
|
+
|
|
1071
|
+
The internal exact-decimal compiler preserves multiple decimal columns and ordinary fields through nonrecursive CTE declarations, including forward references, joins, membership queries, aggregates and set operations. Dependency-driven query-local output metadata and unique lexical names preserve nested scope without changing the table registry. Nonrecursive dependency cycles fail explicitly. Flat encoding stages avoid native Turso and libSQL nested-CTE failures. Recursive composition and ordinary storage adoption remain separate work.
|
|
1072
|
+
- **Preserve exact decimal values at derived source boundaries** — `@voltro/database`
|
|
1073
|
+
|
|
1074
|
+
Add SQL encoding for logical decimal result sources without floating-point conversion. Source-local stages work across SQLite, native Turso and libSQL, preserve nulls and reject malformed values, overflow and nonzero excess fractional digits. This is an internal boundary for subsequent CTE integration; ordinary decimal storage is unchanged.
|
|
1075
|
+
- **Verify an exact SQLite decimal physical schema and validation boundary** — `@voltro/database`
|
|
1076
|
+
|
|
1077
|
+
Add internal physical type, default and CHECK helpers for the candidate sortable decimal representation. Precision and scale live in the TEXT-affinity type name because native Turso omits custom type arguments from schema introspection. Constraints reject malformed keys, numeric writes, out-of-range values and NUL suffixes. Normal schema generation, store encoding and migration behavior remain unchanged until the complete storage contract is integrated.
|
|
1078
|
+
- **Compose exact decimal compound queries across SQLite clients** — `@voltro/database`
|
|
1079
|
+
|
|
1080
|
+
The internal exact-decimal query compiler now composes set operations across different decimal scales without floating-point conversion. Numeric deduplication, ordering, branch pagination and aggregate results retain exact values. Flatter result stages avoid libSQL parser stack exhaustion. Ordinary decimal storage remains unchanged pending the complete migration and storage contract.
|
|
1081
|
+
- **Compose exact decimal membership and existence subqueries** — `@voltro/database`
|
|
1082
|
+
|
|
1083
|
+
The internal exact-decimal query compiler supports non-correlated IN, NOT IN, EXISTS and NOT EXISTS in source predicates and aggregate filters. Shared output metadata preserves cross-scale membership and SQL NULL semantics without numeric casts. Shallower column decoding avoids libSQL parser stack exhaustion. Ordinary storage remains unchanged pending full adoption.
|
|
1084
|
+
- **Keep framework table metadata bound to first-party source** — `@voltro/cli`
|
|
1085
|
+
|
|
1086
|
+
Framework table nullability metadata stays tied to first-party source declarations. Compiled native database workers retain their existing relocation and exact-value guarantees.
|
|
1087
|
+
- **Execute real Helm chart rendering in CI** — `@voltro/cli`
|
|
1088
|
+
|
|
1089
|
+
Install pinned Helm in CI so Kubernetes chart rendering tests execute against the real tool instead of reporting an unavailable-tool diagnostic.
|
|
1090
|
+
- **Verify media prompt changes across process restart** — `@voltro/ai`
|
|
1091
|
+
|
|
1092
|
+
Add a persistent SQL regression that kills the original media workflow process and resumes its journaled enqueue in a fresh process with a corrected prompt, with an offline provider and a new-execution control.
|
|
1093
|
+
- **Resolve Timeline tests from their dedicated SQL Server fixture** — `@voltro/cli`
|
|
1094
|
+
|
|
1095
|
+
SQL Server Timeline integration tests reuse the dedicated test stack credential when no explicit test override is configured. No copied credential, runtime authentication change or availability skip is introduced.
|
|
1096
|
+
- **Native restore coverage shares SQL Server fixture configuration** — `@voltro/cli`
|
|
1097
|
+
|
|
1098
|
+
Native SQL restore coverage reads the shared SQL Server fixture configuration consistently with the other integration checks.
|
|
1099
|
+
- **Run native SQL tests with the declared fixture and capability lanes** — `@voltro/sql-mssql`, `@voltro/sql-turso`
|
|
1100
|
+
|
|
1101
|
+
SQL Server row-image, decimal-query and Change Tracking regressions borrow the shared test-stack credential instead of skipping when its optional environment override is absent. Decimal conversion registers only applicable native Turso schemas; existing real-engine rejection suites retain the unsupported syntax checks. The skipped-test gate remains unchanged.
|
|
1102
|
+
- **Preserve scheduled validation evidence across concurrent updates** — `@voltro/cli`
|
|
1103
|
+
|
|
1104
|
+
Scheduled validation runs retain their results while newer updates are checked independently. Superseded ordinary checks still cancel within their event lane.
|
|
1105
|
+
- **Match the production deploy fixture pool to SQL workflow startup** — `@voltro/cli`
|
|
1106
|
+
|
|
1107
|
+
Run the production deploy integration fixture with the three connections required by SQL workflow startup, preserving the real production storage and deploy assertions.
|
|
1108
|
+
- **Protect retained decimal-conversion witnesses** — `@voltro/database`
|
|
1109
|
+
|
|
1110
|
+
Internal conversion source, stage and publication snapshots refuse row writes and retain their exact protection schema. Staging, retries, rollback and cleanup verify artifact protection. Cutover and rollback compare live values and rowids directly against protected snapshots, retaining allocation-watermark guards.
|
|
1111
|
+
|
|
1112
|
+
Content and physical-identity capture are paged. Durable identity and validated conversion-decision receipts store counts and digests rather than row arrays. Staging, publication, rollback and cleanup bind protected-source validation to the schema epoch inside the write transaction, preserving exact artifact ownership and late-change refusal without row-sized snapshot predicates. Initial source capture validates and copies through one pinned transaction, avoiding a row-sized SQL predicate between separate preflight and write phases. Stage validation balances conjunctions so larger reviewed sets do not exceed native Turso's expression-depth limit. Stage protection is removed only inside the guarded publication transaction; failed publication restores it and successful publication leaves the live table writable. Older mutable internal stages are refused rather than reused for image receipt capture.
|
|
1113
|
+
|
|
1114
|
+
Publication carries the schema epoch captured before complete protected-source and stage validation. It no longer repeats every reviewed row predicate in its write batch, and never resets the epoch after validation. The replaced row-literal guard builders and their whole-row collection branches are removed; snapshot verification uses the current paged digest readers.
|
|
1115
|
+
|
|
1116
|
+
Public conversion activation remains pending. Per-row stage statements and in-memory conversion-review/staging payloads still require bounded representation work.
|
|
1117
|
+
|
|
1118
|
+
Stage content checks now group at most 32 predicates per SQL statement, including all groups during creation and reuse. This bounds expression growth with row count; total staging arrays and individual wide keys remain separate limits.
|
|
1119
|
+
|
|
1120
|
+
Stage row decisions and grouped validations are generated incrementally from the sorted review instead of retaining duplicate full-table maps and SQL arrays. The reviewed cells remain materialized.
|
|
1121
|
+
|
|
1122
|
+
Stage creation now executes generated statements incrementally on one pinned transaction connection. Failure rolls back rows, schema, protection and ownership together; the foreign-key checks remain inside that transaction. The reviewed-cell input remains materialized.
|
|
1123
|
+
|
|
1124
|
+
Decimal source observations use bounded keyset pages with an overlap row, preserving exact typed composite identities and rejecting duplicates at page boundaries. Returned reviewed-cell arrays still require bounded storage work.
|
|
1125
|
+
|
|
1126
|
+
Retained-source coverage consumes observation pages directly and aborts before fetching later pages when validation fails. Explicit reviewed decisions remain indexed until complete coverage is proven; no full observed-cell list or separate observed-identity set is retained by this path.
|
|
1127
|
+
|
|
1128
|
+
Decision normalization is shared per cell, and witnesses can consume validated decisions incrementally while preserving the existing digest. Duplicate or unordered streamed identities invalidate the witness instead of accepting a partial prefix.
|
|
1129
|
+
|
|
1130
|
+
An internal transaction-owned review importer stores individually normalized decisions under an exact canonical index, hashes bounded pages, rejects duplicate imports and seals accepted rows against DML. Integration with source/stage lifecycle and ordinary migrations remains pending.
|
|
1131
|
+
|
|
1132
|
+
Stored-review verification checks the exact ownership receipt, both schemas and protection sets, canonical row keys/observations, bounded content hash and a stable schema epoch. Empty forged sort keys cannot hide before the first page. Ownership rows receive their own DML protection.
|
|
1133
|
+
|
|
1134
|
+
The internal stored-review verifier can now match retained-source observations through indexed batches of at most 128 decisions. It verifies the persisted receipt before coverage and rejects missing, extra, stale, shape-mismatched or ambiguous source decisions, plus schema changes during coverage. This prerequisite does not yet replace the normal conversion path’s in-memory review arrays.
|
|
1135
|
+
|
|
1136
|
+
Stored decisions can also be consumed as complete rows across page boundaries using the same canonical scanner. Consumers run inside the pinned transaction, preserve callback errors, and reject schema changes during consumption; stage lifecycle adoption remains pending.
|
|
1137
|
+
|
|
1138
|
+
Review stores can be reopened from the reviewed owner and witness after closing and recreating the client. Reopening verifies known complete table/index/protection definitions, including native Turso’s canonical trigger spelling, instead of accepting the schema claimed by a mutable receipt. Normal migration lifecycle adoption remains pending.
|
|
1139
|
+
|
|
1140
|
+
Retained decimal sources now create or reopen a deterministically owned review store inside source preparation’s transaction and use its bounded source-coverage verifier. The source receipt binds the review artifact identity. Publication artifact catalogs and atomic cleanup include both review tables. Stage array consumption and later publication-resume content verification are still being completed.
|
|
1141
|
+
|
|
1142
|
+
Decimal stage insertion and row validation now consume the retained source’s persisted review directly, including complete rows spanning decision pages. Stage manifests use that accepted witness, final validation uses a pinned transaction, and the obsolete array-row generator is removed.
|
|
1143
|
+
|
|
1144
|
+
Publication retry, rollback and cleanup now verify persisted review contents against the submitted review witness, not only artifact catalogs. Verification shares the protected-snapshot connection and pre-write schema epoch. Native Turso’s known trigger serialization after table rename is recognized independently from the original stored schema receipt; arbitrary catalog changes remain rejected.
|
|
1145
|
+
|
|
1146
|
+
Internal decimal migration reviews now accept repeatable iterables in unique canonical decision order. Source retry and publication identities hash normalized decisions incrementally rather than creating sorted arrays. Initial import verifies that its second read matches the first witness and rolls back one-shot or unstable input; ordinary file-migration wiring remains pending.
|
|
1147
|
+
|
|
1148
|
+
Removed the superseded in-memory review coverage map and array normalizer. Composite-key coverage regressions now execute the persisted-store verifier against real adapters; cell normalization and streaming witness tests remain.
|
|
1149
|
+
|
|
1150
|
+
An internally reviewed SQLite decimal publication can be applied again after a completed rollback. The previous owned receipt and retained artifacts are checked and retired before a fresh source capture. Reapplication validates the current source against the reviewed cells, preserves intervening unrelated field updates, and refuses stale conversion decisions without overwriting live values.
|
|
1151
|
+
|
|
1152
|
+
Internal retained decimal source and review artifacts use the reserved framework table namespace. Ordinary schema planning therefore keeps the recovery receipts and snapshots instead of treating them as undeclared application tables to drop.
|
|
1153
|
+
- **Share dedicated SQL Server test fixture credentials** — `@voltro/database`, `@voltro/cli`
|
|
1154
|
+
|
|
1155
|
+
Share dedicated SQL Server fixture credentials across integration suites and execute soft-drop restoration whenever the test service is reachable; retain explicit connection overrides through Turbo.
|
|
1156
|
+
- **Indexed source maps use bounded section offsets**
|
|
1157
|
+
|
|
1158
|
+
Source-map processing uses the repaired dependency version that rejects invalid indexed section offsets instead of blocking the event loop.
|
|
1159
|
+
- **Prepare a bounded SQL Server Timeline capture plan** — `@voltro/cli`
|
|
1160
|
+
|
|
1161
|
+
Generate schema-aware transactional capture triggers gated by owned, ready metadata and a matching live observer window. Apply the image budget to the complete before/after pair after privacy masking; incomplete events retain neither image. Real SQL Server tests cover bulk writes, rollback, identity changes and composed Inspect replay using the generated plan and decoder. Guarded installation, continuity checks and ordinary host activation remain separate work; no serving path installs this plan automatically.
|
|
1162
|
+
- **Prepare SQL Server Timeline physical catalog validation** — `@voltro/cli`
|
|
1163
|
+
|
|
1164
|
+
Add a schema-scoped catalog fingerprint for application and capture tables. Record column shape, defaults, computed expressions, indexes, constraints and trigger definitions without incorporating mutable data or identity counters. Reject missing tables and opaque metadata. Object-scoped VIEW DEFINITION is required for this reader. Ownership validation, installation and serving activation remain separate prerequisites.
|
|
1165
|
+
- **Fence SQL Server Timeline reads against concurrent table resets** — `@voltro/cli`
|
|
1166
|
+
|
|
1167
|
+
Add an internal generation guard around unpublished coherent reads. Reject stale generations and concurrent table or journal resets. Refuse admission until a committed tracking version is later than every tracked table's start version: freshly enabled Change Tracking alone cannot distinguish all resets. Real SQL Server tests cover concurrent truncation, tracking interruption and table replacement. Migration preparation and serving activation remain pending.
|
|
1168
|
+
- **Prepare SQL Server Timeline continuity checks** — `@voltro/cli`
|
|
1169
|
+
|
|
1170
|
+
Add a read-only internal generation reader combining tracked-table identity and Change Tracking start version with the database recovery fork. Missing or invisible metadata cannot establish continuity. Native SQL Server probes verify ordinary writes, TRUNCATE, tracking gaps, table replacement and full backup restoration. Permission checks, migration-owned preparation and guarded serving activation remain separate work; this does not enable a serving capture path.
|
|
1171
|
+
- **Prepare guarded SQL Server Timeline source reads** — `@voltro/cli`
|
|
1172
|
+
|
|
1173
|
+
Read journal events and snapshot rows through one transaction-bound reader. Check the observer contract, catalog, generation and live pruning receipts on both sides of the snapshot, rejecting expiry during a row read. Failed reads do not acknowledge or consume events. Recorder session publication and ordinary backend activation remain pending.
|
|
1174
|
+
- **Compose SQL Server Timeline installation and namespace discovery** — `@voltro/cli`
|
|
1175
|
+
|
|
1176
|
+
Compose owned capture retirement, preparation and persisted activation under the caller's migration coordinator. Reuse a valid active contract without resetting its observers; retire stale capture before rebuilding. Resolve physical table schemas through SQL Server object lookup, including default-schema fallback, and reject mixed or unavailable namespaces.
|
|
1177
|
+
|
|
1178
|
+
Ordinary migration and serving backend selection remain unchanged.
|
|
1179
|
+
- **Prepare SQL Server Timeline journal frames** — `@voltro/cli`
|
|
1180
|
+
|
|
1181
|
+
Add an internal SQL Server journal reader using the coherent read transaction. Share retained-history validation with MySQL/MariaDB, including visible source positions, acknowledged pruning and complete row images. Actual Inspect tests keep a historical replay stable across committed updates, inserts and deletes. Capture provisioning, continuity guards and ordinary host activation remain separate work; this does not enable SQL Server source capture in serving paths.
|
|
1182
|
+
- **Preserve migration lock scope in SQL Server Timeline sessions** — `@voltro/cli`
|
|
1183
|
+
|
|
1184
|
+
Allow SQL Server Timeline maintenance and recorder sessions to use the migration coordinator's configured lock scope independently of physical table schema. Propagate that scope through admission, acknowledgements, retention and closure. Ordinary backend selection remains unchanged while its integration is prepared.
|
|
1185
|
+
- **Prepare SQL Server Timeline observer admission** — `@voltro/cli`
|
|
1186
|
+
|
|
1187
|
+
Add tracked lease storage and migration-coordinated observer admission and closure. Validate the ready epoch, generation and physical catalog before opening immutable server-clock capture windows. Drain in-flight writers, retain history protected by another live observer, and establish a fresh baseline after observer/window expiry. Closing is idempotent and scoped to its observer. ACK, retention and session publication remain pending; ordinary serving and migration backend selection are unchanged.
|
|
1188
|
+
- **Prepare SQL Server Timeline observer acknowledgements and retention** — `@voltro/cli`
|
|
1189
|
+
|
|
1190
|
+
Add revision-fenced acknowledgements, immutable renewal windows and bounded journal retention for the internal SQL Server Timeline foundation. Expired observers cannot renew; retention invalidates lagging observers before deleting unseen events. Surviving observers receive explicit pruning receipts, including when collection occurs between reading and acknowledging a frame.
|
|
1191
|
+
|
|
1192
|
+
Source/session publication and final activation remain pending. Ordinary dashboard backend selection and migration hooks are unchanged.
|
|
1193
|
+
- **Prepare persisted SQL Server Timeline activation contracts** — `@voltro/cli`
|
|
1194
|
+
|
|
1195
|
+
Persist the prepared generation and physical catalog contract when activating owned Timeline storage. Reject drift between preparation and activation, and discover serving contracts without adopting the current generation on failure. Retirement clears activation. Extend the unactivated internal storage plan to carry the contract; ordinary migration and backend selection remain unchanged.
|
|
1196
|
+
- **Persist and retire SQL Server Timeline capture plans** — `@voltro/cli`
|
|
1197
|
+
|
|
1198
|
+
Store the hashed capture plan with internal metadata so migration retirement does not require an obsolete application registry. Validate it before touching triggers. Retire under the migration coordinator: rotate the epoch, clear capture windows and history, and remove only verified triggers in one transaction after in-flight writers drain. Preparing a changed plan is allowed only after explicit retirement. Native tests cover plan changes, removed tables, failed subsequent DDL and altered-trigger refusal. Serving activation remains pending.
|
|
1199
|
+
- **Prepare a coherent SQL Server Timeline read boundary** — `@voltro/cli`
|
|
1200
|
+
|
|
1201
|
+
Add an internal driver-owned read transaction for SQL Server Timeline source work. Bind isolation, lock timeout and observer deadlock priority to each read RPC, reject nested transactions, and discard conflicting frames completely. Real SQL Server tests cover pooled state, interruption, caller transaction ownership and a coherent retry after concurrent writes. This does not activate SQL Server Timeline source capture or change ordinary host behavior.
|
|
1202
|
+
- **Prepare scoped SQL Server Timeline recorder sessions** — `@voltro/cli`
|
|
1203
|
+
|
|
1204
|
+
Serialize coherent source reads, decode complete batches and acknowledge them before recorder publication. Scope closure and failed or interrupted reads retire the recorder identity and close the observer lease. Internal polling renews the observer and collects changes while the session is open.
|
|
1205
|
+
|
|
1206
|
+
Ordinary backend wiring, snapshot projection and final activation remain pending.
|
|
1207
|
+
- **Prepare schema-aware SQL Server Timeline images** — `@voltro/cli`
|
|
1208
|
+
|
|
1209
|
+
Add internal SQL Server image expressions and decoder support for exact decimal and bigint text, UTC timestamps, JSON scalars, unusual column names and masked fields. Reject malformed JSON and non-null unsupported storage before capture publication. This prepares generic source capture; migration provisioning, complete-event size limits and ordinary host activation remain separate work.
|
|
1210
|
+
- **Compose guarded SQL Server Timeline schema preparation** — `@voltro/cli`
|
|
1211
|
+
|
|
1212
|
+
Compose application, storage and trigger guards into migration-coordinated capture preparation. Create missing matching objects, enable table tracking and commit the control update required by the generation fence. Stay preparing; ready capture must be retired explicitly. Native tests cover retry, concurrent preparation, missing-trigger recovery and rollback on denied trigger creation. Recognize SQL Server's own tracking child objects while rejecting unexpected dependencies. Preserve original failures alongside transaction-cleanup failures. Observer admission, retention and serving activation remain pending.
|
|
1213
|
+
- **Rediscover SQL Server Timeline activation after session invalidation** — `@voltro/cli`
|
|
1214
|
+
|
|
1215
|
+
Resolve persisted activation when opening a new observer instead of retaining a fixed startup contract. Running recorder sessions can establish a new baseline after migration or later provisioning without serving-side DDL. Require callers to provide the migration lock scope explicitly. Ordinary backend wiring remains pending.
|
|
1216
|
+
- **Prepare SQL Server Timeline snapshot projection** — `@voltro/cli`
|
|
1217
|
+
|
|
1218
|
+
Read Timeline snapshot pages through the coherent source transaction using the journal's exact scalar encoding, privacy rules and row decoder. Unsupported, malformed or oversized images reject the entire snapshot. Scoped recorder sessions now expose table reads backed by this projection.
|
|
1219
|
+
|
|
1220
|
+
Final activation and ordinary backend wiring remain pending.
|
|
1221
|
+
- **Guard SQL Server Timeline storage adoption** — `@voltro/cli`
|
|
1222
|
+
|
|
1223
|
+
Add a read-only migration-side guard for existing capture storage. Require complete metadata visibility, the expected private table shape and primary keys, and one valid framework owner record. Refuse collisions, incomplete storage, altered identity settings and unexpected dependent objects without repairing or deleting them. Trigger ownership and guarded installation remain separate prerequisites; serving capture is not activated.
|
|
1224
|
+
- **Validate SQL Server Timeline application-table prerequisites** — `@voltro/cli`
|
|
1225
|
+
|
|
1226
|
+
Add a read-only, schema-scoped preflight for capture identity and projected columns. Reject nullable, computed, nonunique, disabled, filtered or compound identities, and reject storage types incompatible with the image encoders. A unique id index may contain included columns, but Change Tracking also needs an enabled primary key. Native SQL Server tests cover these constraints and same-name tables in other schemas. Exact physical-shape fingerprints, private storage ownership and installation remain prerequisites to serving activation.
|
|
1227
|
+
- **Validate SQL Server Timeline trigger ownership** — `@voltro/cli`
|
|
1228
|
+
|
|
1229
|
+
Validate application capture triggers against their recorded generated plan before migration-side adoption. Reject changed bodies, parents, execution options, disabled triggers and colliding objects without modifying them. A ready capture requires all triggers; a preparing installation may have missing ones. Plan upgrades still require explicit retirement. This does not install triggers or activate serving capture.
|
|
1230
|
+
|
|
1231
|
+
---
|
|
1232
|
+
|
|
42
1233
|
## [0.83.1] — 2026-10-04
|
|
43
1234
|
|
|
44
1235
|
### Added
|