@voltro/cli 0.5.0 → 0.7.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 +68 -1
- package/bin/voltro.mjs +63 -2
- package/dist/apiBuild-CSM74oEZ.js +2 -0
- package/dist/{apiBuild-CZugTK46.js → apiBuild-deIjJKJm.js} +2 -1
- package/dist/bin.js +2 -2
- package/dist/{commands-CJheDVOr.js → commands-BHdc71DG.js} +2671 -2960
- package/dist/dev-BYfrYwrT.js +2 -0
- package/dist/dev-Bqyq62Ak.js +6492 -0
- package/dist/devActivity-1WtIVyHc.js +153 -0
- package/dist/devActivity.d.ts +46 -0
- package/dist/devActivity.js +2 -0
- package/dist/index.js +1 -1
- package/dist/inspectMetrics-BLUBAWLb.js +3711 -0
- package/dist/serveCommand-Dn6--0Bv.js +1111 -0
- package/dist/serveEntry.js +3 -2
- package/dist/start-CWZW_4Et.js +941 -0
- package/dist/startEntry.d.ts +5 -0
- package/dist/startEntry.js +3 -0
- package/package.json +22 -17
- package/templates/AGENTS.core.md +36 -1
- package/templates/AGENTS.md +36 -1
- package/templates/agent-docs/_manifest.json +3 -3
- package/templates/agent-docs/ai.md +35 -3
- package/templates/agent-docs/authentication.md +264 -0
- package/templates/agent-docs/cli.md +126 -3
- package/templates/agent-docs/configuration.md +38 -0
- package/templates/agent-docs/data.md +616 -40
- package/templates/agent-docs/database/querying.md +151 -3
- package/templates/agent-docs/database/schema.md +1 -0
- package/templates/agent-docs/deployment.md +171 -0
- package/templates/agent-docs/internationalization.md +2 -0
- package/templates/agent-docs/plugins.md +4 -4
- package/templates/agent-docs/reference.md +56 -2
- package/templates/agent-docs/templates/apibackends.md +13 -2
- package/templates/agent-docs/testing.md +215 -3
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/app.config.ts +20 -13
- 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/.env.example +10 -4
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-data-advanced/app.config.ts +21 -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/app.config.ts +15 -2
- 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-search/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/app.config.ts +20 -1
- package/templates/apps/api-webhooks/package.json +8 -8
- 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-blank/package.json +7 -7
- 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-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/baselines/bare/.env.example +10 -3
- package/templates/baselines/compose/.env.example +10 -2
- package/templates/baselines/compose-mariadb/.env.example +10 -2
- package/templates/baselines/helm/.env.example +10 -3
- package/dist/apiBuild-D-iBYKa3.js +0 -2
- package/dist/dev-B71ICpXJ.js +0 -9896
- package/dist/dev-DNUetyG2.js +0 -2
- package/dist/serveCommand-C0WFA396.js +0 -1084
- package/templates/apps/api-auth/.env +0 -17
- package/templates/apps/api-data-advanced/.env +0 -17
- package/templates/apps/api-governance/.env +0 -4
- package/templates/apps/api-webhooks/.env +0 -6
|
@@ -765,12 +765,13 @@ It covers both halves of the stack:
|
|
|
765
765
|
| Scope | It notices | Reach for |
|
|
766
766
|
|---|---|---|
|
|
767
767
|
| server | `if (!rows[0]) throw …` | `.one()` / `.first()` |
|
|
768
|
-
| server | 3+ sequential `store.query` in one handler | `relations()` + `.with()` |
|
|
768
|
+
| server | 3+ sequential `store.query` in one handler | `relations()` + `.with()` — or `Effect.all` |
|
|
769
769
|
| server | `Effect.promise(() => ctx.store.…)` | `yield* EffectStore` |
|
|
770
770
|
| server | `requireScope(...)` at the top of an executor | `guards:` on the descriptor |
|
|
771
771
|
| server | a `token` / `secret` / `password` column with no encryption | `.encrypted()` |
|
|
772
772
|
| server | a notify / webhook helper called at a mutation's tail | `defineSubscriber` / `defineReaction` |
|
|
773
773
|
| server | `hasMore` + `limit + 1` | `paginateById` |
|
|
774
|
+
| server | `.getTime()` / `.toISOString()` mapping a row on the way out | `rowSchema(table)` as the descriptor's `output` |
|
|
774
775
|
| client | per-field `useState` + a submit flag | `useFormBinding` |
|
|
775
776
|
| client | a table with local sort/filter state | `useDataTable` |
|
|
776
777
|
| client | `FileReader` / `readAsDataURL` | `useUpload` |
|
|
@@ -783,6 +784,71 @@ The rules are deliberately conservative — a detector that cries wolf trains yo
|
|
|
783
784
|
to ignore it. A column that already carries `.encrypted()`, or a handler that
|
|
784
785
|
already uses `.one()`, stays silent.
|
|
785
786
|
|
|
787
|
+
Two of them are worth spelling out, because their advice is not one-line:
|
|
788
|
+
|
|
789
|
+
**The credential-column rule skips names that aren't credentials.** A name ending
|
|
790
|
+
in `Id` / `_id`, a name ending in `Hash` / `_hash`, and a name beginning with
|
|
791
|
+
`vault` are all left alone:
|
|
792
|
+
|
|
793
|
+
| Column | Why it's skipped |
|
|
794
|
+
|---|---|
|
|
795
|
+
| `jiraSecretId`, `token_id` | an IDENTIFIER of a secret held elsewhere, not the secret |
|
|
796
|
+
| `apiKeyHash`, `password_hash` | the hash IS the protection — encrypting it is nonsense, and it breaks the column as a unique lookup key |
|
|
797
|
+
| `vaultToken` | a HANDLE into a secret store, naming a secret held elsewhere |
|
|
798
|
+
|
|
799
|
+
The suffix tests use a camelCase / underscore boundary on purpose: a blind
|
|
800
|
+
`/id$/i` would also swallow `apiKeyValid`, while `tokenIdentifier` — which ends
|
|
801
|
+
in neither — must still fire.
|
|
802
|
+
|
|
803
|
+
**The sequential-reads rule names TWO levers, and the criterion for choosing.**
|
|
804
|
+
Both shapes chain later reads off earlier results, so no text-level heuristic can
|
|
805
|
+
split them — you make the call:
|
|
806
|
+
|
|
807
|
+
- The reads are a **parent → child walk on ONE key** → declare `relations()` in a
|
|
808
|
+
`*.relations.ts` and collapse them into `.with({ … })`: one JSON-aggregate
|
|
809
|
+
query, on every dialect.
|
|
810
|
+
- The reads **collect ids from SEVERAL sources** (JSON-array references, a
|
|
811
|
+
junction carrying extra columns, JS-side sorting) → keep the assembly and run
|
|
812
|
+
the independent LEADING reads under `Effect.all`. Same queries, same results,
|
|
813
|
+
only concurrent — zero parity risk.
|
|
814
|
+
|
|
815
|
+
The second case is the common one. Measured on a real 74-hit codebase, about two
|
|
816
|
+
handlers were clean full-parity `relations()` conversions and the other ~72 were
|
|
817
|
+
multi-source assemblies where `.with()` covers only part of the work or subtly
|
|
818
|
+
changes behaviour. Prescribing `relations()` for all of them would be wrong ~97%
|
|
819
|
+
of the time — and advice that is usually wrong trains you to ignore the finding.
|
|
820
|
+
|
|
821
|
+
The human view shows the first three file paths per finding and says how many it
|
|
822
|
+
withheld. Those paths are the actionable part — a count you cannot turn back
|
|
823
|
+
into a work list tells you the size of the problem, not how to fix it — and the
|
|
824
|
+
matching rule lives inside the CLI, so you cannot re-derive the list with your
|
|
825
|
+
own grep. `--json` prints the complete scan, nothing elided, with no preflight
|
|
826
|
+
output mixed in:
|
|
827
|
+
|
|
828
|
+
```bash
|
|
829
|
+
voltro doctor . --json # the complete scan: every file path, machine-readable
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
```json
|
|
833
|
+
{
|
|
834
|
+
"root": "/app/api",
|
|
835
|
+
"scannedFiles": 214,
|
|
836
|
+
"scannedDirs": ["queries", "mutations", "database"],
|
|
837
|
+
"findings": [
|
|
838
|
+
{
|
|
839
|
+
"id": "row-not-found",
|
|
840
|
+
"scope": "server",
|
|
841
|
+
"smell": "hand-written not-found branch on rows[0]",
|
|
842
|
+
"use": ".one() — fails with the typed NoRowFound on zero rows AND on more than one",
|
|
843
|
+
"files": ["queries/team.get.ts", "queries/user.get.ts", "…"]
|
|
844
|
+
}
|
|
845
|
+
]
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
That is the form to hand an agent, or to pipe into a script that works the list
|
|
850
|
+
file by file.
|
|
851
|
+
|
|
786
852
|
## `voltro capabilities` — what the framework actually exports
|
|
787
853
|
|
|
788
854
|
Asked "what does this framework export", a language model will produce a
|
|
@@ -1756,7 +1822,7 @@ _voltro update — bump the framework to the latest version and run the codemods
|
|
|
1756
1822
|
`voltro update` upgrades an app to the latest framework release. It does three things in order:
|
|
1757
1823
|
|
|
1758
1824
|
1. **Bump** every `@voltro/*` dependency in `package.json` to the target version.
|
|
1759
|
-
2. **Install** with your package manager
|
|
1825
|
+
2. **Install** with your project's package manager — see [Which package manager](#which-package-manager) below.
|
|
1760
1826
|
3. **Run the codemods** shipped with the target version — automatic source rewrites for any breaking API change, plus printed manual steps for anything that can't be automated.
|
|
1761
1827
|
|
|
1762
1828
|
```bash
|
|
@@ -1765,12 +1831,47 @@ voltro update --to 0.4.0 # pin an explicit target version
|
|
|
1765
1831
|
voltro update --dry-run # preview the bump + which codemods would run — writes nothing
|
|
1766
1832
|
voltro update --force # allow a dirty working tree (not recommended)
|
|
1767
1833
|
voltro update --exact # pin exact versions (drop the ^ / ~ range prefix)
|
|
1834
|
+
voltro update --help # every flag — always answered, even on a dirty tree
|
|
1768
1835
|
|
|
1769
1836
|
# Recover the codemods after a MANUAL version bump (no bump, no install):
|
|
1770
1837
|
voltro update --codemods-only --from 0.3.0 # re-run codemods 0.3.0 → installed
|
|
1771
1838
|
voltro update --codemods-only --from 0.3.0 --to 0.4.0 # explicit delta
|
|
1772
1839
|
```
|
|
1773
1840
|
|
|
1841
|
+
## In a workspace, the whole workspace moves
|
|
1842
|
+
|
|
1843
|
+
Run `voltro update` anywhere inside a workspace — a `pnpm-workspace.yaml`, or a
|
|
1844
|
+
`workspaces` field in an ancestor `package.json` — and **every member
|
|
1845
|
+
`package.json` that declares `@voltro/*` is bumped to the same version**, with
|
|
1846
|
+
the install running **once at the workspace root**.
|
|
1847
|
+
|
|
1848
|
+
This is not a convenience. Your api and your web app share generated types (the
|
|
1849
|
+
rpcGroup) and a session cookie shape; if the api moves to 0.6.0 while
|
|
1850
|
+
`apps/web` and `packages/ui-*` stay on 0.5.0, the mismatch shows up as a runtime
|
|
1851
|
+
decode error in the browser, not as a build failure. Half-upgraded is the worst
|
|
1852
|
+
state to be in, so `voltro update` never leaves you there.
|
|
1853
|
+
|
|
1854
|
+
The plan output — and `--dry-run` — lists every file it will touch:
|
|
1855
|
+
|
|
1856
|
+
```text
|
|
1857
|
+
voltro update: 0.5.0 → 0.6.0
|
|
1858
|
+
workspace: /repo (4 package.json with @voltro/* deps)
|
|
1859
|
+
package.json
|
|
1860
|
+
@voltro/cli: ^0.5.0 → ^0.6.0
|
|
1861
|
+
apps/api/package.json
|
|
1862
|
+
@voltro/cli: ^0.5.0 → ^0.6.0
|
|
1863
|
+
@voltro/database: ^0.5.0 → ^0.6.0
|
|
1864
|
+
apps/web/package.json
|
|
1865
|
+
@voltro/client: ^0.5.0 → ^0.6.0
|
|
1866
|
+
packages/ui-admin/package.json
|
|
1867
|
+
@voltro/web: ~0.5.0 → ~0.6.0
|
|
1868
|
+
package manager: pnpm
|
|
1869
|
+
install runs in: /repo
|
|
1870
|
+
```
|
|
1871
|
+
|
|
1872
|
+
A standalone (non-workspace) project is unaffected: its own `package.json`, its
|
|
1873
|
+
own install, in place.
|
|
1874
|
+
|
|
1774
1875
|
## Already bumped by hand? Recover the codemods
|
|
1775
1876
|
|
|
1776
1877
|
If you bump `@voltro/*` versions in `package.json` yourself and install first, a
|
|
@@ -1793,9 +1894,31 @@ source version.
|
|
|
1793
1894
|
|
|
1794
1895
|
Codemods **rewrite your source**, so you need a clean diff to review afterwards. `voltro update` refuses to run on a dirty git working tree — commit or stash first. Use `--dry-run` to preview without touching anything, or `--force` to override the guard (you accept a mixed diff).
|
|
1795
1896
|
|
|
1897
|
+
`--help` / `-h` is answered *before* the guard, so `voltro update --help` prints the flag list even on a dirty tree. The same holds for `voltro doctor --help`.
|
|
1898
|
+
|
|
1899
|
+
## If the install fails
|
|
1900
|
+
|
|
1901
|
+
The bump is written before the install runs, so a failed install leaves your `package.json` on the target version — and **no codemods applied**. `voltro update` says so explicitly, because the codemods for a jump ship *inside* the target version: a failed install never put them on disk, so there is nothing that could have run them. Fix the install, run it, then apply the codemods you are missing with the command the failure message prints for you:
|
|
1902
|
+
|
|
1903
|
+
```bash
|
|
1904
|
+
voltro update --codemods-only --from 0.5.0 --to 0.6.0
|
|
1905
|
+
```
|
|
1906
|
+
|
|
1796
1907
|
## What gets bumped
|
|
1797
1908
|
|
|
1798
|
-
Every `@voltro/*` entry in `dependencies` and `devDependencies
|
|
1909
|
+
Every `@voltro/*` entry in `dependencies` and `devDependencies` — in every workspace member, see above — with the range style preserved (`^0.3.0` stays caret, `~0.3.0` stays tilde) unless you pass `--exact`. Non-registry specs (`workspace:*`, `catalog:`, `link:`, …) are left untouched — they're already resolved by your monorepo or catalog.
|
|
1910
|
+
|
|
1911
|
+
## Which package manager
|
|
1912
|
+
|
|
1913
|
+
`voltro update` never assumes npm. It resolves your project's package manager in this order, starting in the app directory and walking **up to the repo root**:
|
|
1914
|
+
|
|
1915
|
+
1. The **`packageManager` field** in a `package.json` (the corepack standard) — authoritative, wins over any lockfile.
|
|
1916
|
+
2. A **lockfile** at that level — `pnpm-lock.yaml`, `yarn.lock`, `bun.lock` / `bun.lockb`, `package-lock.json`.
|
|
1917
|
+
3. **npm**, only when nothing declares one.
|
|
1918
|
+
|
|
1919
|
+
Walking up matters in a workspace: a scaffolded Voltro project keeps its lockfile at the monorepo root, so running `voltro update` from `apps/api` still finds `pnpm` rather than falling back to npm and running `npm install` against a pnpm workspace.
|
|
1920
|
+
|
|
1921
|
+
The same resolved manager is used for the **registry lookup** of the latest version (`pnpm view`, `yarn npm info`, `bun pm view`), so a private or scoped registry configured in your `.npmrc` / `.yarnrc.yml` is honored. `npm view` is only a last-resort fallback.
|
|
1799
1922
|
|
|
1800
1923
|
## Codemods
|
|
1801
1924
|
|
|
@@ -91,6 +91,44 @@ build-time constant, still works.)
|
|
|
91
91
|
| `envVar.boolean(opts)` | `boolean` | `true/1/yes/on` ↔ `false/0/no/off/""` |
|
|
92
92
|
| `envVar.url(opts)` | `string` | validated with `URL` |
|
|
93
93
|
| `envVar.enum([…] as const, opts)` | literal union | must be one of the values |
|
|
94
|
+
| `envVar.secret(opts)` | `string` | server-only, required, length floor |
|
|
95
|
+
|
|
96
|
+
### `envVar.secret` — for values that must not be guessable
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
env: defineEnv({
|
|
100
|
+
// Ours to invent → `voltro dev` mints one per project.
|
|
101
|
+
VOLTRO_SESSION_SECRET: envVar.secret({ generate: 'base64url' }),
|
|
102
|
+
// Someone else's to issue → must be fetched, never invented.
|
|
103
|
+
STRIPE_SECRET_KEY: envVar.secret({ minLength: 20 }),
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It differs from `envVar.string({ access: 'secret' })` in three ways, each
|
|
108
|
+
closing a specific failure:
|
|
109
|
+
|
|
110
|
+
- **`access` is forced to `'secret'`** — a value with a length floor is never
|
|
111
|
+
something you meant to bundle into a browser.
|
|
112
|
+
- **There is no `default`.** A default secret is not a secret: every
|
|
113
|
+
deployment that forgot to set the variable would share it.
|
|
114
|
+
- **A length floor** (default 32). Presence alone does not catch the real
|
|
115
|
+
failure mode — a variable that is *set* but reads `change-me` signs forgeable
|
|
116
|
+
cookies while looking completely healthy.
|
|
117
|
+
|
|
118
|
+
**`generate` opts a variable into per-project minting.** On first `voltro dev`,
|
|
119
|
+
any declared-but-unset mintable secret is written to a gitignored `.env.local`
|
|
120
|
+
and the boot continues. That is why no Voltro template ships a secret value: a
|
|
121
|
+
placeholder in a template is a signing key published to everyone who downloads
|
|
122
|
+
it, and it passes every check you could write.
|
|
123
|
+
|
|
124
|
+
Leave `generate` off for anything a third party issues. A WorkOS API key is
|
|
125
|
+
just as secret and just as required, but inventing one produces a value that
|
|
126
|
+
merely *looks* right and authenticates nobody — better that the boot gate fails
|
|
127
|
+
and a human fetches the real one.
|
|
128
|
+
|
|
129
|
+
Minting is **development only**. `voltro serve`, `build` and `start` never mint:
|
|
130
|
+
in production a missing secret is a boot failure, which is the whole point.
|
|
131
|
+
Generate deployment values with `voltro secret generate <purpose>`.
|
|
94
132
|
|
|
95
133
|
Every builder takes `{ access, optional?, default?, description?, example? }`.
|
|
96
134
|
Use `default` for a fallback (the value type stays present, `A`); use
|