@voltro/cli 0.30.1 → 0.30.2
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 +48 -0
- package/dist/{apiBuild-DmkzRyv7.js → apiBuild-BUS1RfPi.js} +2 -2
- package/dist/apiBuild-DMlxvL8h.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/checkCommand-CARZ36Tr.js +2 -0
- package/dist/{checkCommand-C4konUy9.js → checkCommand-D_wdOFhw.js} +346 -293
- package/dist/{commands-BkJft9K2.js → commands-BgGAUcku.js} +1680 -1680
- package/dist/{dbCommand-qn__kLEt.js → dbCommand-CbPRCD1x.js} +56 -19
- package/dist/dbCommand-Ht47LrV-.js +2 -0
- package/dist/{dev-C_NPcLDp.js → dev-GAOIot9G.js} +2169 -2153
- package/dist/{dev-DXhhZ7-f.js → dev-tPcOmjPJ.js} +1 -1
- package/dist/{frameworkTableAssembly-Cpgn2e88.js → frameworkTableAssembly-BwIrO5nv.js} +122 -103
- package/dist/frameworkTableAssembly-D-EebUQX.js +2 -0
- package/dist/index.js +1 -1
- package/dist/{inspectMetrics-CkB2J0Zn.js → inspectMetrics-BM1vrsga.js} +314 -314
- package/dist/{sdkgen-wsWLrq4l.js → sdkgen-BV4fFucu.js} +1 -1
- package/dist/{serveCommand-HtI4X5kj.js → serveCommand-ClqXCX1y.js} +17 -16
- package/dist/serveEntry.js +2 -2
- package/dist/{start-egkbpugc.js → start-CTnNvKlk.js} +348 -348
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +4 -2
- package/templates/agent-docs/internationalization.md +25 -3
- package/templates/agent-docs/whats-new.md +24 -47
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +10 -10
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +9 -9
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +2 -2
- package/dist/apiBuild-Ca6Sq5jy.js +0 -2
- package/dist/checkCommand-DHD0aBeR.js +0 -2
- package/dist/dbCommand-wfGi3FgG.js +0 -2
- package/dist/frameworkTableAssembly-Bn_c4Mnl.js +0 -2
package/dist/startEntry.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t } from "./start-
|
|
1
|
+
import { tt as e } from "./inspectMetrics-BM1vrsga.js";
|
|
2
|
+
import { t } from "./start-CTnNvKlk.js";
|
|
3
3
|
export { e as loadDotEnv, t as runStartCommand };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.2",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -62,22 +62,22 @@
|
|
|
62
62
|
"@effect/platform-node": "^0.108.0",
|
|
63
63
|
"@effect/sql": "^0.52.0",
|
|
64
64
|
"@effect/workflow": "^0.19.0",
|
|
65
|
-
"@voltro/ai": "0.30.
|
|
66
|
-
"@voltro/cache": "0.30.
|
|
67
|
-
"@voltro/data-transfer": "0.30.
|
|
68
|
-
"@voltro/database": "0.30.
|
|
69
|
-
"@voltro/env": "0.30.
|
|
70
|
-
"@voltro/kv": "0.30.
|
|
71
|
-
"@voltro/logger": "0.30.
|
|
72
|
-
"@voltro/plugin-auth": "0.30.
|
|
73
|
-
"@voltro/plugin-broadcast": "0.30.
|
|
74
|
-
"@voltro/plugin-mail": "0.30.
|
|
75
|
-
"@voltro/plugin-storage": "0.30.
|
|
76
|
-
"@voltro/plugin-webhooks": "0.30.
|
|
77
|
-
"@voltro/protocol": "0.30.
|
|
78
|
-
"@voltro/runtime": "0.30.
|
|
79
|
-
"@voltro/serverless": "0.30.
|
|
80
|
-
"@voltro/workflow": "0.30.
|
|
65
|
+
"@voltro/ai": "0.30.2",
|
|
66
|
+
"@voltro/cache": "0.30.2",
|
|
67
|
+
"@voltro/data-transfer": "0.30.2",
|
|
68
|
+
"@voltro/database": "0.30.2",
|
|
69
|
+
"@voltro/env": "0.30.2",
|
|
70
|
+
"@voltro/kv": "0.30.2",
|
|
71
|
+
"@voltro/logger": "0.30.2",
|
|
72
|
+
"@voltro/plugin-auth": "0.30.2",
|
|
73
|
+
"@voltro/plugin-broadcast": "0.30.2",
|
|
74
|
+
"@voltro/plugin-mail": "0.30.2",
|
|
75
|
+
"@voltro/plugin-storage": "0.30.2",
|
|
76
|
+
"@voltro/plugin-webhooks": "0.30.2",
|
|
77
|
+
"@voltro/protocol": "0.30.2",
|
|
78
|
+
"@voltro/runtime": "0.30.2",
|
|
79
|
+
"@voltro/serverless": "0.30.2",
|
|
80
|
+
"@voltro/workflow": "0.30.2",
|
|
81
81
|
"chokidar": "^5.0.0",
|
|
82
82
|
"ioredis": "^5.11.1",
|
|
83
83
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.md
CHANGED
|
@@ -652,7 +652,7 @@ each plugin's own README.
|
|
|
652
652
|
|
|
653
653
|
| Topic | Open | Summary |
|
|
654
654
|
|---|---|---|
|
|
655
|
-
| **What's new in 0.30.
|
|
655
|
+
| **What's new in 0.30.2** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
656
656
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
657
657
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
658
658
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -9,7 +9,7 @@ each plugin's own README.
|
|
|
9
9
|
|
|
10
10
|
| Topic | Open | Summary |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| **What's new in 0.30.
|
|
12
|
+
| **What's new in 0.30.2** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
13
13
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
14
14
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
15
15
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -1193,8 +1193,10 @@ inquiries.publicFeed reviewed=public by design; returns only published rows
|
|
|
1193
1193
|
The reason is required: `reviewed=` with no why is the claim without the
|
|
1194
1194
|
evidence, and doctor refuses it. A bare tag is always available and is the honest
|
|
1195
1195
|
alternative. The two are counted and printed apart, and
|
|
1196
|
-
`--write-authz-allowlist` **
|
|
1197
|
-
|
|
1196
|
+
`--write-authz-allowlist` is **additive**: it keeps the existing file verbatim —
|
|
1197
|
+
entries, comments, grouping, order — and appends only tags it does not already
|
|
1198
|
+
contain. It cannot remove a line. Removing one is your edit, or it happens on its
|
|
1199
|
+
own when an executor gains a guard and its line stops mattering.
|
|
1198
1200
|
|
|
1199
1201
|
#### Reading the whole list
|
|
1200
1202
|
|
|
@@ -29,8 +29,8 @@ export default {
|
|
|
29
29
|
type: 'web' as const,
|
|
30
30
|
name: 'myApp',
|
|
31
31
|
port: 5191,
|
|
32
|
-
//
|
|
33
|
-
//
|
|
32
|
+
// The languages this app serves. Drives `<html lang>`, the resolved
|
|
33
|
+
// locale, and the `locale` your page `meta` receives.
|
|
34
34
|
locales: ['en', 'de'] as const,
|
|
35
35
|
// Fallback locale when no cookie / Accept-Language matches a
|
|
36
36
|
// supported one. MUST be in `locales`. Pick your source-of-truth
|
|
@@ -39,7 +39,29 @@ export default {
|
|
|
39
39
|
}
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
The framework's generated `.framework/app.tsx` wraps the Router in `<I18nProvider>` automatically
|
|
42
|
+
The framework's generated `.framework/app.tsx` wraps the Router in `<I18nProvider>` automatically when your app **ships catalogs**. You don't write the provider yourself; you don't import `@voltro/i18n` in your layout.
|
|
43
|
+
|
|
44
|
+
### Declaring languages is not the same as adopting the catalogs
|
|
45
|
+
|
|
46
|
+
`locales:` states which languages the app serves. Whether `@voltro/i18n` gets
|
|
47
|
+
wired is decided separately, by whether `src/locales/<code>.ts` exists for every
|
|
48
|
+
listed code:
|
|
49
|
+
|
|
50
|
+
| `locales:` | `src/locales/*` | what you get |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| declared | present | `<I18nProvider>` wired, `useT()` works, plus the language facts |
|
|
53
|
+
| declared | absent | the language facts ONLY — `<html lang>`, the resolved locale, `meta.locale`. Bring your own i18n stack. |
|
|
54
|
+
| absent | — | everything falls back to `'en'` |
|
|
55
|
+
|
|
56
|
+
The second row exists because the two used to be one switch, and that made
|
|
57
|
+
`locales:` unusable for an app with its own i18n: declaring it demanded catalog
|
|
58
|
+
files and failed the boot, so the option got left out — and then `<html lang>`
|
|
59
|
+
was `"en"` and every page's `meta` received `locale: 'en'`, for every visitor, in
|
|
60
|
+
an app that is not English. That is worse than useless: a page that trusted the
|
|
61
|
+
value would have rendered the wrong language.
|
|
62
|
+
|
|
63
|
+
`voltro dev` prints which mode it is in, so a declared-but-unwired app is never a
|
|
64
|
+
silent surprise.
|
|
43
65
|
|
|
44
66
|
## Locale resolution order
|
|
45
67
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# What's new in 0.30.
|
|
1
|
+
# What's new in 0.30.2
|
|
2
2
|
|
|
3
3
|
Read this FIRST when a task touches an area you have not worked in recently.
|
|
4
4
|
It is the cheapest way to notice that the framework grew the thing you were
|
|
@@ -7,69 +7,46 @@ workaround for something that shipped two versions ago.
|
|
|
7
7
|
|
|
8
8
|
BREAKING entries name a codemod; run `voltro update` to apply it.
|
|
9
9
|
|
|
10
|
-
###
|
|
11
|
-
|
|
12
|
-
- **@voltro/testing, @voltro/sql-mysql** — **One integration suite was never skip-guarded, and the guard it called silently ran it anyway.** `describeIfAvailable(label, dependency, probe, suite)` was called with three arguments in `sql-mysql`'s `fileMigrationLedger.mariadb` suite, so `probe` bound to the SUITE body: `await probe()` executed it at file scope, its `beforeAll` and `test`s registered outside any `describe` and ran unconditionally, and the `describe.skipIf` underneath registered an empty shell. The file therefore passed when MariaDB happened to be up and hard-failed with `SqlError: MysqlClient: Failed to connect` when it was not — the exact opposite of the clean skip it was written to have, and a red `pnpm test` for anyone without the docker stack.
|
|
13
|
-
|
|
14
|
-
`tsc` could not catch it: every dialect package's tsconfig `include` lists the src glob only, so `__tests__/` is not typechecked at all — the same gap that lets an incomplete parity fixture compile. So the arity is checked at runtime now, and `describeIfAvailable` throws a `TypeError` naming what it got instead of quietly running the suite. One of roughly twenty call sites was wrong; nineteen were right, which is why nothing looked off.
|
|
15
|
-
- **@voltro/cli** — **Four things `voltro doctor` knew and would not tell you.** All reported by a consumer, all measured rather than guessed.
|
|
16
|
-
|
|
17
|
-
**The authz list was reachable by no route at all.** The human view truncated at 20 (`… and 14 more`) and `--json` had no `authz` section — measured, its keys were `root · scannedFiles · … · serverOnly`. Reading findings 21..n meant allowlisting the first 20, re-running, and resetting the file: a loop to read a list the tool already had. `--json` carries `authz` now (`counts`, `guardVocabulary`, `allowlist`, and every `unchecked` finding, never truncated), and the elision line names both the command and the field. The same defect, one section over, is recorded in `serverOnly`'s own comment — "the field was MISSING from `--json` entirely" — so this is that lesson applied rather than re-learned. Both surfaces read ONE scan (`scanAuthzForRoot`), because two derivations of one scan is how two views come to disagree about what was found.
|
|
18
|
-
|
|
19
|
-
**The allowlist could not tell "reviewed and safe" from "debt".** Its header says `This is DEBT, not approval` — which is right, and which made it the wrong place for the other thing people legitimately need to record: an executor a human has read and found genuinely open, constrained by something the scanner cannot see. It was also the ONLY place, so the reporter resorted to comment blocks around groups of lines — a convention inside a file parsed line by line, which the next `--write-authz-allowlist` would have flattened without a word. A line is now either `<tag>` (debt, unchanged) or `<tag> reviewed=<why>`, the reason REQUIRED — `reviewed=` with no why is the claim without the evidence and is refused, since a bare tag is the honest alternative and always available. Doctor counts and prints the two apart, and `--write-authz-allowlist` preserves reviewed lines instead of downgrading them.
|
|
20
|
-
|
|
21
|
-
**A hint that named 41% of the files was not a hint.** One hand-roll finding listed 2 641 of 6 374 files, and the reporter skipped the whole section because of it — including the lines pointing at 5 and 13 files, which were worth acting on that day. Findings now print FEWEST files first, and a finding above both a share (20%) and a floor (50 files) prints its ADVICE without the enumeration, marked as a codebase-wide pattern. Both bounds matter: the share is what makes it a pattern, the floor keeps a small app — where "3 of 8 files" is a large share and a perfectly readable list — out of it. The paths stay in `--json`.
|
|
10
|
+
### Changed
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
- **@voltro/cli** — **`encryptSteps` was derived twice, once per boot path.** Six hand-mirrored lines in `dev.ts` and in `serveApi.ts` — read the flow control off the definition, compare `=== true`, build the cipher, spread the result or nothing. They agreed today and nothing kept them agreeing, which is the shape that produced the `_voltro_outbox` error loop and every dev/serve scar in `packages/cli/CLAUDE.md`. The asymmetry a drift would produce here is the bad direction: step payloads encrypted under `voltro dev` and plaintext under `voltro serve`, with the declaration reading as protection in both.
|
|
12
|
+
- **@voltro/plugin-notifications** — **The docs argued for `resolveSubjectId` using a rationale its own source had retracted.** The page justified the option with a consumer's measurement — rows belonging to people with no auth user, which employee-keying would "reach" and `subject.id` would not — and cited their number. That team then reversed the decision and wrote the correction themselves: *the subject is whatever signs in; if your addressing unit is not that, you are addressing something nobody can read.* An inbox belongs to whoever can OPEN it, and only an account can. Keying by employee never delivered those rows — it made them look addressed, and charged a translation on every read path and every push.
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
- **@voltro/runtime, @voltro/cli, @voltro/workflow** — **`debounce` never ran. Neither did a `batch` that flushed on its timeout — and `batch` could not be started at all.** Three defects, one boundary, all found by a consumer who adopted flow control against a live API and measured `attempts: 13, collapsed: 14, runs: 0` on a debounced workflow that never produced a run.
|
|
14
|
+
So the framework was teaching, in a permanent document, a lesson that the one team who had lived it had withdrawn. That is worse than an out-of-date example: it is an argument with a measurement attached, which is the most persuasive kind and here the wrong one.
|
|
28
15
|
|
|
29
|
-
|
|
16
|
+
The page and the option's docstring now lead with the caution, say which question to ask first ("can the thing I am addressing sign in?"), point at translating once at the SENDING seam, and keep `resolveSubjectId` recommended for the case it was actually built for — an app whose sign-in identity genuinely IS its own id, which is a different situation from a second identity some accounts happen to map to.
|
|
30
17
|
|
|
31
|
-
|
|
18
|
+
No behaviour change; the option works exactly as before.
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
**Why no unit suite could see any of this.** `admissionDrainer.test.ts` fakes `startAdmitted`; `workflowRuntime.test.ts` fakes `admitStart`. Each is a complete test of its own half, and the defect lived strictly between them — the same shape as this repo's dev/serve parity scars, one level down. `flowControlDrainRoundTrip.test.ts` wires a real gate to a real facade over a real in-memory store and drives all four deferring controls from arrival to run. It was written red: debounce and batch-timeout failed, throttle and concurrency passed, which is exactly the diagnosis. It asserts the pending row is CONSUMED rather than merely that a run eventually happened — a debounce that re-collapses twice on the way is still broken, and `admitted: 1` alone would not say so.
|
|
36
|
-
- **@voltro/cli** — **`_voltro_outbox` was polled every five seconds by apps that never had it created.** Reported by a consumer as a permanent `Table doesn't exist` loop — and, they noted, "a permanent error loop that buries the real ones". The table appeared zero times in `voltro db plan`, which was correct for the gate as written and wrong for what the boot actually does.
|
|
37
|
-
|
|
38
|
-
The two predicates had drifted. The table was created when the app declared at least one `*.outbox.ts` handler; the delivery worker was STARTED when at least one handler existed *including the framework's own* `voltro.webhook.emit`, which is registered whenever the app has a webhook surface. So an app with webhooks and no handler file got the worker, got `ctx.outbox`, and got a `ctx.webhooks.emit` inside a mutation writing through a table that was never planned. The 0.30.0 note claiming such an app "keeps the in-memory callback" described the intent, not the code.
|
|
39
|
-
|
|
40
|
-
Widening the table's gate to match could not work: the webhook surface includes outgoing webhooks declared on EVENTS, and the migration path detects features by walking filenames, so it cannot see them without loading the app's modules. **`_voltro_outbox` and `_voltro_outbox_attempts` are therefore created for every sql app now** — small, dialect-neutral, empty unless something enqueues, the same trade `_voltro_wakeups` and the storage tables already take. Two empty tables against a class of divergence that has no symptom until production.
|
|
41
|
-
|
|
42
|
-
It also fixes `voltro migrate`, which never passed the flag at all — and, in the same sweep, `voltro migrate` never detected `*.connection.ts` either, so the credential-vault tables were created by `voltro dev` / `voltro db apply` and silently not by `voltro migrate`. Migrate carried its own copy of the feature-detection walk; it calls the shared `detectFeatureMix` now, so there is one walk and one answer.
|
|
20
|
+
### Fixed
|
|
43
21
|
|
|
44
|
-
|
|
22
|
+
- **@voltro/cli** — **`voltro doctor --write-authz-allowlist` deleted the list it exists to protect.** Measured by a consumer against their real file, one run, nothing else in between: 2 363 lines / 2 294 debt / 5 reviewed / 2 comment blocks before, and 31 lines / 0 debt / 5 reviewed / 0 comment blocks after. `doctor` went from exit 0 to exit 1 reporting 2 294 × "no access check" — executors that had been recorded as debt and were now recorded nowhere. They restored from backup.
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
- **@voltro/web** — **The second half of the SSR `useId` divergence: the client boot rendered a sibling to the app that the server did not.** `VoltroRuntimeProvider` renders `{children}` alongside a chrome slot (`chromeMounted ? <>…overlays…</> : null`), while the server rendered the page tree with no boot wrapper at all. A parent with two children forks React's tree-id path; a parent with one does not — so this shifted every `useId` in the app exactly as the router provider did, one level further up.
|
|
24
|
+
The writer built the file from the scan's CURRENT `unchecked` set, and an executor already in the allowlist is classified `allowlisted`, not `unchecked` — so it appears in neither input. A first write looks perfect; the SECOND one empties the file. That hole predates the debt/reviewed split (the old writer was `formatAllowlist(res.unchecked.map(f => f.tag))`, identical shape). Rescuing `reviewed` and not debt did not cause the loss but INVERTED it: the five explained lines survived and the 2 294 unexplained ones did not, which is backwards. A reviewed line at least names a human who can be asked again; a lost debt line names nobody. The reporter's sentence is the one to keep — **the debt is the part you must not lose.**
|
|
48
25
|
|
|
49
|
-
|
|
26
|
+
The writer is **additive and byte-preserving** now. The existing file is kept verbatim — entries, comments, grouping, order — and only tags it does not already contain are appended under a marker saying nothing above it was touched. Removal is no longer a capability of this command; it is a hand edit, or it happens on its own when an executor gains a guard and its line stops mattering. The reporter's comment blocks survive as a consequence of the file not being rebuilt, not as a special case.
|
|
50
27
|
|
|
51
|
-
The
|
|
28
|
+
The message changed too, because the old one was true and told nobody: `wrote 0 debt tag(s)` becomes `+0 new, 2299 kept, 0 removed`, and a no-op says so. `removed` is reported although it is structurally always zero — a success line has to name the quantity that changed.
|
|
52
29
|
|
|
53
|
-
|
|
54
|
-
- **@voltro/
|
|
30
|
+
Also from the same report: a credential-shaped column that is ALSO `.unique()` is now its own finding (`plaintext-secret-lookup-key`) rather than being told to use `.encrypted()`. Encryption and equality lookup are mutually exclusive unless the cipher is deterministic, so the ordinary advice would make the column unfindable; the new advice names the hash-as-lookup-key shape the framework's own `_voltro_api_keys.hashedKey` uses, and the deterministic-cipher trade. Requested by the reporter, who hit it on two of five flagged columns.
|
|
31
|
+
- **@voltro/cli** — **The three items the previous round left open, closed.**
|
|
55
32
|
|
|
56
|
-
|
|
33
|
+
**An `app.config.ts` that cannot be imported silently produced an app with NO plugins — and a schema that proposes dropping every plugin table.** A consumer measured 517 tables becoming 506 when they added ONE import, with six `matches no declared table` warnings and a `pluginRef` FATAL naming a plugin that had nothing to do with the cause. The reader was `catch { return [] }`. That is not a smaller schema, it is a wrong one: plugins contribute their tables through `extendSchema.tables`, so zero plugins means every one of those tables reads as undeclared, and the differ plans a DROP. `dev.ts` carries a long comment about this exact class after it cost a release — written beside `loadApiConfigDiagnosed`, while this sibling reader kept the swallow, which is the argument for one reader rather than two. It throws `AppConfigLoadError` now, naming the consequence and the fact that the failure ran at config-EVALUATION time; `voltro check` reports it and continues with an explicit "this report is INCOMPLETE" rather than a silent empty list. The reporter's own caution is carried into the code: they measured the IMPORT and the CALL, not the library, so this is not about any one package — any module doing something non-trivial when first evaluated reaches it.
|
|
57
34
|
|
|
58
|
-
|
|
35
|
+
**`locales:` no longer forces you to adopt the framework's i18n.** Declaring `locales: ['de','en']` demanded `src/locales/<code>.ts` and killed the boot with `Failed to resolve import "../src/locales/de"`, so an app with its own i18n stack had to leave the option out — and then got `<html lang="en">` and `locale: 'en'` handed to every page's `meta`, for every visitor, in a German-language app. The parameter was not merely useless there, it was misleading: trusting it would have turned every German tab English after hydration. The two decisions are separate now. `locales:` declares the languages (`<html lang>`, the resolved locale, `meta.locale`); the PRESENCE of `src/locales/*` decides whether `<I18nProvider>` is wired. An app that ships catalogs is unchanged. An app that does not gets a boot line saying which mode it is in — silently not wiring a provider would have replaced one surprise with another.
|
|
59
36
|
|
|
60
|
-
**
|
|
37
|
+
**The `raw-fetch` advisory no longer reads sharper than its evidence.** It led with "no SSRF guard", flatly. A consumer opened all 13 of their call sites and measured that every host was a compile-time constant, the two dynamic URLs were built from a constant base, and every user-supplied URL already went through their own wrapper — "the remaining value is traceparent + retry + http.allowHosts, not security". Counted against this repo: 132 raw `fetch(` sites, 64 with a literal URL. So the smell now states what is true of every site (the framework client is not being used) and scopes the SSRF half to where the URL is not a constant. Being sharper than the evidence has a specific cost: it teaches the reader to skim the one line that would have named the genuinely user-supplied URL.
|
|
38
|
+
- **@voltro/cli, @voltro/web, @voltro/database, @voltro/plugin-notifications** — **Seven findings a consumer had carried for two to four releases, all measured by them, all closed.**
|
|
61
39
|
|
|
62
|
-
|
|
40
|
+
**No page could set its SSR `<title>`.** The generated shell bakes `<title>{app name}</title>`, and the render's head was APPENDED before `</head>` — so a response carried two title elements, and `document.title` is the FIRST per the HTML spec. The app name won on every server-rendered page; the real title appeared only after hydration. Everything that reads HTML without executing it — link previews, crawlers, a bookmark taken before hydration, a screen reader announcing the document — saw the app name. There was no app-side workaround: `.framework/index.html` is regenerated every boot and its title comes from `name`, which is one value per app. **FIVE call sites** spliced a head by hand (`ssrShell`, the static prerender and the SPA shell in `build.ts`, and two in `start.ts`); the fifth was found by the guard written for the first four. One `mergeHeadInjection` now, which replaces the shell's title when the render brought one. Verified in a real chromium against a real `voltro dev`: exactly one `<title>`, and it is the page's.
|
|
63
41
|
|
|
64
|
-
|
|
42
|
+
**`voltro db scan-credentials` died on postgres — the default dialect, against a framework table.** It asked for a column named `subject`; `_voltro_row_history` has `subjectId`, `actor` and `scope`. Two defects, and the second is the one to keep: only the `COUNT(*)` probe was inside the try, so a missing COLUMN escaped as an unhandled error — jumping clean over this module's own "a missing target reports as missing" promise and over `scanExitCode`'s exit-2-for-a-vacuous-run. Both defaults and both guards are fixed; all three subject-shaped columns are scanned, an unavailable column reports as unavailable, and a missing table is said ONCE rather than once per column. Verified against live postgres: a planted credential in `actor` exits 1, an empty schema exits 2 with "NOTHING WAS SCANNED".
|
|
65
43
|
|
|
66
|
-
|
|
67
|
-
- **@voltro/web, @voltro/cli** — **The server render discarded the request's query string.** `RenderPageOptions` had no `search`, and the SSR router context hardcoded `search: ''` with a comment noting that the client reads `window.location.search` on hydration. That is true, and it is precisely why the hardcoding was wrong: the client reading the real value is what turns a discarded query string into a divergence in an exported context value. The value was already computed in both per-request boot paths — the loaders receive it — and simply never reached the renderer.
|
|
44
|
+
**`doctor` described constrained executors as unconstrained.** An app that registers `setRowFilter` had all four of its filtered executors reported as "nothing constraining WHICH row" — for handlers that are, measured, already narrowed to the caller's own rows. It cannot be downgraded to a pass (the filter is a function from table to predicate; nothing static can say whether it covers a given table), but the wording pushed the reader toward the allowlist, where the line later reads as "checked and accepted" while meaning "the tool could not see it". The finding now names the row filter and what to verify, and the summary says once that the scan is blind there.
|
|
68
45
|
|
|
69
|
-
|
|
46
|
+
**A standing DELETE never introduced itself.** Retention is registered by plugins the app never wrote a line about, with defaults in months, and nothing announced it at boot — a consumer lost 1 944 freshly-migrated rows to a 180-day default, then got it wrong a second time by setting the env var in a running pod rather than a file. One line per policy now: the table, the age, the column it is measured on, whether it is conditional, and the variable that changes it.
|
|
70
47
|
|
|
71
|
-
**
|
|
48
|
+
**The notification inbox had no index for its own two read paths.** It declared `byInboxSubject(subjectId)` while the plugin itself issues `subjectId = ? ORDER BY createdAt DESC LIMIT ?` and `subjectId = ? AND readAt IS NULL`. Both are declared now, the unread one PARTIAL — 3 of 2 475 rows were unread in their fullest inbox.
|
|
72
49
|
|
|
73
|
-
|
|
50
|
+
**Four notification routes were served and unreachable.** `archive`, `unarchive`, `markUnread` and `markAllRead` were registered as routes and missing from `notificationsRpcClientImports`, whose comment said "kept in lockstep with the exports". A comment is not a lockstep; `rpcSurfaceLockstep.test.ts` counts both sides.
|
|
74
51
|
|
|
75
|
-
|
|
52
|
+
**Nothing reported a page with no `meta`.** 75 of 243 pages across three of their apps had none — never, not since a migration — each serving the app name as its title. `voltro doctor` reports them now. Their own first gate is worth repeating: it keyed on pages rendering a particular wrapper and ran green while 60 pages without it had no title at all. The condition is the PAGE, not the wrapper it happens to use.
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/ai": "0.30.
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/protocol": "0.30.
|
|
20
|
-
"@voltro/runtime": "0.30.
|
|
15
|
+
"@voltro/ai": "0.30.2",
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/protocol": "0.30.2",
|
|
20
|
+
"@voltro/runtime": "0.30.2",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.30.
|
|
24
|
+
"@voltro/testing": "0.30.2",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-auth": "0.30.
|
|
20
|
-
"@voltro/protocol": "0.30.
|
|
21
|
-
"@voltro/runtime": "0.30.
|
|
22
|
-
"@voltro/sql-postgres": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-auth": "0.30.2",
|
|
20
|
+
"@voltro/protocol": "0.30.2",
|
|
21
|
+
"@voltro/runtime": "0.30.2",
|
|
22
|
+
"@voltro/sql-postgres": "0.30.2",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.30.
|
|
26
|
+
"@voltro/testing": "0.30.2",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
20
|
-
"@voltro/protocol": "0.30.
|
|
21
|
-
"@voltro/runtime": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
20
|
+
"@voltro/protocol": "0.30.2",
|
|
21
|
+
"@voltro/runtime": "0.30.2",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.30.
|
|
25
|
+
"@voltro/testing": "0.30.2",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
27
|
"vitest": "^4.1.10"
|
|
28
28
|
}
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-deactivation": "0.30.
|
|
20
|
-
"@voltro/protocol": "0.30.
|
|
21
|
-
"@voltro/runtime": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-deactivation": "0.30.2",
|
|
20
|
+
"@voltro/protocol": "0.30.2",
|
|
21
|
+
"@voltro/runtime": "0.30.2",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.30.
|
|
25
|
+
"@voltro/testing": "0.30.2",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
27
|
"vitest": "^4.1.10"
|
|
28
28
|
}
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@react-email/components": "^1.0.12",
|
|
15
15
|
"@react-email/render": "^1.4.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-mail": "0.30.
|
|
20
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
21
|
-
"@voltro/protocol": "0.30.
|
|
22
|
-
"@voltro/runtime": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-mail": "0.30.2",
|
|
20
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
21
|
+
"@voltro/protocol": "0.30.2",
|
|
22
|
+
"@voltro/runtime": "0.30.2",
|
|
23
23
|
"effect": "^3.22.0",
|
|
24
24
|
"react": "^19.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/testing": "0.30.
|
|
27
|
+
"@voltro/testing": "0.30.2",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"vitest": "^4.1.10"
|
|
30
30
|
}
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
20
|
-
"@voltro/plugin-storage": "0.30.
|
|
21
|
-
"@voltro/protocol": "0.30.
|
|
22
|
-
"@voltro/runtime": "0.30.
|
|
23
|
-
"@voltro/sql-mysql": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
20
|
+
"@voltro/plugin-storage": "0.30.2",
|
|
21
|
+
"@voltro/protocol": "0.30.2",
|
|
22
|
+
"@voltro/runtime": "0.30.2",
|
|
23
|
+
"@voltro/sql-mysql": "0.30.2",
|
|
24
24
|
"effect": "^3.22.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/testing": "0.30.
|
|
27
|
+
"@voltro/testing": "0.30.2",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"vitest": "^4.1.10"
|
|
30
30
|
}
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/database": "0.30.
|
|
18
|
-
"@voltro/env": "0.30.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
20
|
-
"@voltro/protocol": "0.30.
|
|
21
|
-
"@voltro/runtime": "0.30.
|
|
22
|
-
"@voltro/sql-sqlite": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/database": "0.30.2",
|
|
18
|
+
"@voltro/env": "0.30.2",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
20
|
+
"@voltro/protocol": "0.30.2",
|
|
21
|
+
"@voltro/runtime": "0.30.2",
|
|
22
|
+
"@voltro/sql-sqlite": "0.30.2",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.30.
|
|
26
|
+
"@voltro/testing": "0.30.2",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"test": "voltro test"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@voltro/cli": "0.30.
|
|
15
|
-
"@voltro/database": "0.30.
|
|
16
|
-
"@voltro/env": "0.30.
|
|
17
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
18
|
-
"@voltro/plugin-storage": "0.30.
|
|
19
|
-
"@voltro/protocol": "0.30.
|
|
20
|
-
"@voltro/runtime": "0.30.
|
|
14
|
+
"@voltro/cli": "0.30.2",
|
|
15
|
+
"@voltro/database": "0.30.2",
|
|
16
|
+
"@voltro/env": "0.30.2",
|
|
17
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
18
|
+
"@voltro/plugin-storage": "0.30.2",
|
|
19
|
+
"@voltro/protocol": "0.30.2",
|
|
20
|
+
"@voltro/runtime": "0.30.2",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.30.
|
|
24
|
+
"@voltro/testing": "0.30.2",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -13,19 +13,19 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.30.
|
|
17
|
-
"@voltro/cms": "0.30.
|
|
18
|
-
"@voltro/database": "0.30.
|
|
19
|
-
"@voltro/env": "0.30.
|
|
20
|
-
"@voltro/plugin-auth": "0.30.
|
|
21
|
-
"@voltro/plugin-multitenancy": "0.30.
|
|
22
|
-
"@voltro/protocol": "0.30.
|
|
23
|
-
"@voltro/runtime": "0.30.
|
|
24
|
-
"@voltro/sql-postgres": "0.30.
|
|
16
|
+
"@voltro/cli": "0.30.2",
|
|
17
|
+
"@voltro/cms": "0.30.2",
|
|
18
|
+
"@voltro/database": "0.30.2",
|
|
19
|
+
"@voltro/env": "0.30.2",
|
|
20
|
+
"@voltro/plugin-auth": "0.30.2",
|
|
21
|
+
"@voltro/plugin-multitenancy": "0.30.2",
|
|
22
|
+
"@voltro/protocol": "0.30.2",
|
|
23
|
+
"@voltro/runtime": "0.30.2",
|
|
24
|
+
"@voltro/sql-postgres": "0.30.2",
|
|
25
25
|
"effect": "^3.22.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@voltro/testing": "0.30.
|
|
28
|
+
"@voltro/testing": "0.30.2",
|
|
29
29
|
"typescript": "^6.0.3",
|
|
30
30
|
"vitest": "^4.1.10"
|
|
31
31
|
}
|