@voltro/plugin-audit 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -39,6 +39,69 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.16.0] — 2026-07-27
43
+
44
+ ### Added
45
+
46
+ - **@voltro/protocol, @voltro/runtime, @voltro/cli, @voltro/database** — **A plugin HTTP route reaches the app's DataStore, on `req.store`** — and `voltro db apply` finally honours `VOLTRO_DESTRUCTIVE_OK`.
47
+
48
+ ### `PluginHttpRouteRequest.store`
49
+
50
+ The same seam as `AuthStrategyInput.store`, one layer over, and the same report produced it. An adopter's `auth/db.ts` has five consumers: two are auth strategies and collapsed onto `input.store` exactly as designed; three are plugin HTTP routes and could not, so the second `ManagedRuntime` + `MysqlClient` stayed for them.
51
+
52
+ Login is the sharpest case and it is not exotic: it MUST write (the session row), it cannot be an rpc mutation because it is what mints the cookie, and it is a documented first-class pattern — `@voltro/plugin-auth` ships `handleSignIn` / `handleSignUp` and the reference consumer mounts them on this surface. Every app that does so needed a store the route contract did not give it.
53
+
54
+ It is the BOOT store, through the same lazy getter the auth chain reads — one ref, three consumers now — and `undefined` while it is still being built, so a route should answer rather than throw. It is **not tenant-scoped**: a route serves raw HTTP with no resolved Subject, so a route reading tenant-owned rows must derive and apply that scope itself. That is the price of the surface being raw, and the reason an rpc procedure stays the better home for anything that can be one.
55
+
56
+ ### `db apply` honours `VOLTRO_DESTRUCTIVE_OK`
57
+
58
+ The table-list opt-in shipped on the auto-migrate path only. `voltro db apply` computes its own plan and checked `summary.blocked` directly; its module contained no occurrence of the variable at all. So an app's staging migration Job — running the sanctioned `db apply --plan` form — had **no route through an intentional, declared, `lossy`-classified table drop**, and the release note's own example was a command that ignored the variable it set.
59
+
60
+ Both forms now route through the same helper as the boot gate: every blocked op must be `lossy`, a named scope unblocks only the tables it names, anything still blocked refuses the whole plan. The applier receives the UNBLOCKED plan — passing the still-blocked one would have it refuse a second time, which is the exact bug `unblockLossy` was written for.
61
+
62
+ Worth naming, because it is a boundary of the message-API gate added last release: the variable exists, is spelled correctly, and IS read — by a different command than the one printing it. *"The named API exists"* and *"the named API is reachable from here"* are different claims, and only the first is checkable from a string.
63
+
64
+ ### Fixed
65
+
66
+ - **@voltro/cli, @voltro/database** — **`voltro update --codemods-only` crashed on a file deleted but not staged** — a regression introduced by 0.15.0's own git-based enumeration.
67
+
68
+ `git ls-files --cached` reads the INDEX, and the index still holds a path that is already gone from disk until the deletion is staged. `addSourceFileAtPath` then threw ENOENT, surfacing as a raw ts-morph stack rather than as anything a reader could act on.
69
+
70
+ The state is not exotic — it is what an ordinary mid-work tree looks like, and `--dry-run` is documented as allowed on a dirty tree, which is exactly where an unstaged deletion lives. The command was unusable in the state it explicitly permits. Paths that are gone are now filtered out: there is nothing to rewrite and nothing to report.
71
+
72
+ **A failing DDL statement now travels with the error, not only to stderr.** A soft-drop aborted a boot with a bare `SqlError: Failed to execute statement` — no statement, no table, no operation. The applier wrote the detail with `process.stderr.write` inside a `tapError` and re-raised the ORIGINAL error, so the detail was lost whenever the process aborted before the stream flushed, or whenever the caller rendered the error rather than the console. `migrate.ts` already carried it in the error for file-based migrations; the planner-driven applier did not, so **which path failed decided whether you could see what failed.**
73
+ - **@voltro/cli** — **`voltro update --codemods-only` refused in the one state it exists for.**
74
+
75
+ The clean-tree guard ran before the repair path, so the command whose entire job is finishing an interrupted upgrade refused whenever the tree was dirty. An adopter reported it twice; the second time their tree carried uncommitted work **from the previous upgrade**, so they bumped four manifests by hand and ran the install themselves — the outcome `voltro update` exists to prevent, reached through its own guard.
76
+
77
+ `--force` was always available and is the wrong answer: it is documented as "not recommended", so it reads as an escape hatch rather than as the sanctioned route through a state we explicitly support.
78
+
79
+ `--codemods-only` now **warns** instead of refusing, saying the codemod diff will be mixed in with the existing changes and pointing at `--dry-run` / `--only`. The full update still refuses — bump, install and rewrite in one pass on top of unrelated changes is a diff nobody can read — and its refusal now names the repair path.
80
+
81
+ ### Internal (no consumer-facing effect)
82
+
83
+ - **@voltro/cli** — The admin-import rejection cases share ONE booted server, and report their phase timings.
84
+
85
+ Two release gates have now failed on that single test line, with two different symptoms and neither an auth defect: first a **400** — this server rejects a request whose body framing broke BEFORE routing, so `gate()` never ran — and then, after the body was removed, a **120-second timeout**. A bare `Test timed out` cannot say whether the boot, the request or the close is what hung, which is why the second failure taught us nothing the first hadn't.
86
+
87
+ This file was standing up SIX real `serveApi` instances, each with its own socket and Effect layer, on a machine already running every other package's suite under `turbo --concurrency=2`. The two rejection cases assert nothing about the store, so they now share one boot, and the assertion carries `boot=…ms absent=…ms wrong=…ms` — a future failure names the slow phase instead of just the line number.
88
+
89
+ No product code changed.
90
+ - **@voltro/cli** — The admin-import rejection tests stop uploading an archive they never needed.
91
+
92
+ A release gate failed with `expected 400 to be 401` on `401 without a Bearer token` and did not reproduce in five later runs. It was not an auth defect and not load-flake in the usual sense: measured against a live server on that path, an honest no-auth request answers **401**, a body-less one answers **401**, and one whose `Content-Length` lies — or whose chunked framing ends early — answers **400**, because the HTTP layer rejects broken framing BEFORE routing. `gate()` is the handler's first statement, so when it never runs there is no 401 to give.
93
+
94
+ Those two tests were POSTing the full packed bundle to assert an authorization property that is decided without reading the body at all. The archive proved nothing and made a multi-KB upload a precondition of an auth assertion. They now send no body, which is both flake-free and the sharper claim; the assertions carry the response body so a future mismatch names the responder instead of printing a bare status.
95
+
96
+ No product code changed — the endpoint's behaviour is unaltered.
97
+ - **@voltro/cli** — The admin-import auth test reports WHO answered, not just that the number was wrong.
98
+
99
+ It failed once under full-gate load with `expected 400 to be 401`, and did not reproduce in four subsequent runs (the file alone, the integration group alone, two full suites, a second full gate). The bare status made the log useless: `gate()` is the first statement in `handleAdminImport` and always 401s an absent token, so a 400 proves the request never reached that handler — but nothing in the failure said which handler DID answer.
100
+
101
+ The assertion now carries the response body and the target URL, so the next occurrence names the responder instead of costing an afternoon. No product code changed; the endpoint's behaviour is unaltered.
102
+
103
+ ---
104
+
42
105
  ## [0.15.0] — 2026-07-27
43
106
 
44
107
  ### ⚠ BREAKING
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-audit",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Audit plugin — ships the `audit()` schema mixin (createdAt/updatedAt/createdBy/updatedBy → Actor) plus an optional mutation interceptor that records every call to a configurable sink (console / memory / custom function).",
5
5
  "keywords": [
6
6
  "voltro",
@@ -37,9 +37,9 @@
37
37
  "node": ">=24.0.0"
38
38
  },
39
39
  "dependencies": {
40
- "@voltro/database": "0.15.0",
41
- "@voltro/logger": "0.15.0",
42
- "@voltro/protocol": "0.15.0"
40
+ "@voltro/database": "0.16.0",
41
+ "@voltro/logger": "0.16.0",
42
+ "@voltro/protocol": "0.16.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "effect": "^3.21.4"