@voltro/cli 0.21.0 → 0.22.1
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 +316 -0
- package/dist/apiBuild-COyPDf3R.js +2 -0
- package/dist/{apiBuild-BHOtsQkp.js → apiBuild-DiWxVz-M.js} +2 -2
- package/dist/bin.js +3 -3
- package/dist/{commands-BZoimvvS.js → commands-BxRaIOBG.js} +1548 -1246
- package/dist/dbCommand-CO3eSAZR.js +2 -0
- package/dist/{dbCommand-CDBbZ-Ac.js → dbCommand-DVASmZj2.js} +295 -253
- package/dist/{dev-DKb92NYX.js → dev-BRiPgbKw.js} +1738 -1702
- package/dist/{dev-CZQ73Yoh.js → dev-Dd3EZzj5.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/inspect-BA67TF6v.js +2 -0
- package/dist/inspect-_ldwsAwH.js +945 -0
- package/dist/{inspectMetrics-DMjWBQif.js → inspectMetrics-9ZSuDeqD.js} +148 -115
- package/dist/{manifestBuild-P9yuCY2d.js → manifestBuild-Bs1Uw22_.js} +1 -1
- package/dist/manifestBuild-i-fRHg_H.js +2 -0
- package/dist/{serveCommand-qHJCs0cX.js → serveCommand-DXJOARkO.js} +305 -306
- package/dist/serveEntry.js +2 -2
- package/dist/{start-B1x6Z60h.js → start-CsIjcOi-.js} +3 -3
- 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 +118 -5
- package/templates/agent-docs/data.md +58 -0
- package/templates/agent-docs/database/migrations.md +142 -20
- package/templates/agent-docs/plugins.md +1 -1
- package/templates/agent-docs/schema-driven-ui.md +2 -2
- package/templates/agent-docs/templates/apibackends.md +1 -1
- package/templates/agent-docs/whats-new.md +32 -114
- 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-storage/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-search/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-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/dist/apiBuild-K7M6Rt0U.js +0 -2
- package/dist/dbCommand-oADAj6ez.js +0 -2
- package/dist/inspect-DcZ04OME.js +0 -2
- package/dist/inspect-Dwx0_tUj.js +0 -921
- package/dist/manifestBuild-D1MzJAiQ.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# What's new in 0.
|
|
1
|
+
# What's new in 0.22.1
|
|
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,147 +7,65 @@ 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
|
-
### ⚠ BREAKING
|
|
11
|
-
|
|
12
|
-
- **@voltro/database, @voltro/runtime, @voltro/workflow, @voltro/voltro** — `text().maxLength(n)` on an EXISTING column now actually applies. It planned zero operations and reported "schema is up to date" while the live column stayed `longtext` — the documented remedy for MariaDB's hash long-unique was a silent no-op, which is worse than no remedy because you stop looking.
|
|
13
|
-
|
|
14
|
-
**The differ was not comparing lengths wrongly — it could not see them.** `maxLength` was absent from the schema snapshot entirely: it lived on the column definition, was read only when rendering CREATE DDL, and never reached the comparison. A consumer pinned the mechanism with a contrast: a column bounded AT CREATION was `varchar(64)` (DDL path, fine); one bounded afterwards stayed `longtext` (diff path, blind).
|
|
15
|
-
|
|
16
|
-
It is now carried on both sides — declared from the definition, live from introspection — compared as its own dimension, and rendered by the applier from the declared snapshot (a bare `text` tag would emit DDL that applies successfully and changes nothing, the silent no-op the applier's convergence check exists to catch).
|
|
17
|
-
|
|
18
|
-
**Three guards against the real risk, which is not missing a change but re-emitting one forever:**
|
|
19
|
-
|
|
20
|
-
- Only the VARCHAR family contributes a live length. MariaDB reports `character_maximum_length = 4294967295` for `longtext` and 65535 for `text`; postgres reports NULL. Reading a type's theoretical maximum would make a declared `text()` differ from its own live column on every boot. - Only `text()` columns are compared. `id()` renders as `VARCHAR(64)` on mysql/mariadb while its declaration carries no length — measured on live MariaDB while building this, and it would have emitted an ALTER for every id column, forever. - Never on sqlite (no length-enforced type), and never when the caller omitted the dialect — an unknown dialect behaves exactly as before this field existed.
|
|
21
|
-
|
|
22
|
-
Proven by round-trip suites against live MariaDB and live Postgres: the same schema re-plans to NOTHING, a length change produces exactly one operation on the right column, applying it lands the new width, and re-planning after that is clean.
|
|
23
|
-
|
|
24
|
-
Classification follows the nullability precedent: **widening is `safe`** (no data can be lost), **narrowing is `needs-backfill`** and says so, with the count query to run first. Narrowing is deliberately not blocked — blocking it would leave the remedy just as unusable as the silence did.
|
|
25
|
-
|
|
26
|
-
**Also: `_voltro_api_keys.hashedKey` is now bounded at 64**, since the value is `sha256Hex(token)` and narrowing it can never fail. The other four unbounded unique columns in framework tables are deliberately left alone, each with the reason at the column: `_voltro_kv.key` is the caller's own key, `idempotencyKey` comes from a user-supplied function, and the two workflow `executionId`s have no shape the framework guarantees. A narrowing ALTER that fails on existing data during a framework upgrade is a worse outcome than the index-size concern it would fix — and the MariaDB hash long-unique is harmless on those tables anyway, since `_voltro_*` is filtered out of the binlog reader's include list.
|
|
27
|
-
|
|
28
|
-
**Migration** — `voltro update` prints it (`0.21.0/01_maxlength-now-migrates`, `manual`, and it fires only for projects that declare a bound). Your source does not change; every `.maxLength(n)` already written keeps compiling. What changes is that the next `voltro db apply` — or a `voltro dev` / `voltro serve` boot with auto-migrate — emits ALTERs it used to skip. Run `voltro db plan` first: it prints exactly which columns would be altered without touching anything, and an empty plan means this does not affect you. Widening is `safe` and can simply run; narrowing is `needs-backfill` and the plan carries the count query to run before it.
|
|
29
|
-
|
|
30
|
-
*Why this is `BREAKING` and not `Fixed`: the API is compatible — nothing is removed, renamed or narrowed, and the same call compiles. But an upgrade now performs DDL against YOUR tables that the previous version silently skipped, and on MariaDB `longtext → varchar(n)` is a full table rebuild that locks. The DB-changes-need-no-codemod exemption is written for `_voltro_*` tables riding the differ; this reaches user tables, so the operator deserves the warning at `voltro update` time rather than in a changelog section they may never open.*
|
|
31
|
-
|
|
32
|
-
### Added
|
|
33
|
-
|
|
34
|
-
- **@voltro/cli, @voltro/voltro** — **`voltro schedule run <name>`** fires one scheduled job on demand, against `voltro dev` or `voltro serve`.
|
|
35
|
-
|
|
36
|
-
Asked for by a consumer whose nightly jobs correct business data and whose workaround was: edit the cron expression to a minute out, wait for the reload, put it back. "Run it once now and watch" is a normal thing to want.
|
|
37
|
-
|
|
38
|
-
**Most of it already existed, and that is why it took a measurement to find the gap.** `SchedulerHandle.fireNow` has been there, and so has `POST /_voltro/inspect/schedules/:name/fire`. What was missing was the way in: no CLI verb, and — the part that mattered — **`voltro serve` mounted no inspect surface at all**. Every `/_voltro/inspect/*` route existed only under `voltro dev`, which is the one place a nightly data-correcting job is not running.
|
|
39
|
-
|
|
40
|
-
So production now mounts it. Two things make that safe rather than a new attack surface, and both were checked rather than assumed:
|
|
41
|
-
|
|
42
|
-
- `handleInspectRequest` is **closed by default**: with no `VOLTRO_INSPECT_TOKEN` every request is 401 carrying the remedy, the compare is constant-time, and the token is never minted outside dev. Opening it is an operator's deliberate act. - Only the handlers production can answer TRUTHFULLY are wired. Everything else stays absent and replies "not configured" — an empty array would claim the app has no procedures. `inspectSchedules` is deliberately still absent: dev computes `nextFiringAt` and the EFFECTIVE coordination from its own boot closure, and reporting a guessed coordination mode to a post-deploy gate is worse than reporting nothing.
|
|
43
|
-
|
|
44
|
-
The manifest's `rpc` / `workflows` entries come from a builder both boot paths now share (`inspectEntries.ts`). They are the same facts on both sides, and a second hand-written copy of the descriptor→entry mapping is the shape this repo keeps paying for.
|
|
45
|
-
|
|
46
|
-
**A run id of `null` is reported as its own outcome**, not as success: the run was coordinated away — another replica holds the lock, or `onOverlap: 'skip'` found the previous run still going. Printing "ok" would claim work that never started.
|
|
47
|
-
|
|
48
|
-
**Also fixed, and it affects every command in the inspect family.** `fetchJson` collapsed a non-2xx into the body's `error` field alone, discarding `message`. The surface answers `{ error: <category>, message: <what happened> }`, so firing an unknown schedule printed "fire failed" while the server had said `scheduler.fireNow: unknown schedule "…"`, and a closed surface printed "unauthorized" while the body named the missing env var. It now prefers `message`, then `reason`, then `error` — fixed in the shared fetch rather than per command.
|
|
49
|
-
|
|
50
|
-
Verified end to end against `voltro-starter/apps/v-api-durable`: the fire returns a run id and the job's own output appears in the server log; an unknown name reports the server's reason; `--format json` round-trips.
|
|
51
|
-
|
|
52
10
|
### Fixed
|
|
53
11
|
|
|
54
|
-
- **@voltro/
|
|
55
|
-
|
|
56
|
-
Write attribution is now CARRIED down the write path instead of re-read from the ambient async-local scope, so a connection-pool handoff can no longer strand a write's `traceId` / `subjectId`.
|
|
57
|
-
|
|
58
|
-
`routeEvent` read the identity with `attributionFields()`, and it runs after `await runPromise(...)`. `AsyncLocalStorage` propagates through continuations the current context CREATES; one scheduled by ANOTHER context — exactly what a pool handoff does when an acquisition queues — resumes with that other context's store. So under contention the write landed with the identity absent. Absent is a LEGAL value there meaning "no request behind this write", so the result does not look like a defect: it looks like a schedule. In a compliance trail that asymmetry is the whole problem, and it is why this is closed structurally rather than left as unlikely.
|
|
59
|
-
|
|
60
|
-
The value is captured ONCE at each public store method, before any await (`withCapturedAttribution`), and threaded explicitly through every `execute*` and into `routeEvent` — in all four dialect stores, including the transactional view, which is the path every framework mutation takes. The ambient scope is kept as a FALLBACK: a site that has not been threaded behaves exactly as before rather than worse, which is what made the change verifiable site by site.
|
|
61
|
-
|
|
62
|
-
**On what is and is not proven.** A pool handoff cannot be reproduced deterministically from a test — the resume context is the driver's choice. Two attempts are worth recording because both produced misleading green: a load-based regression test passed in isolation and failed in the full gate twice (a coin flip that also blocked releases), and a "deterministic" replacement that left the scope before the write finished PASSED against the unthreaded store, because the store re-enters its own scope internally. It proved nothing while looking like proof, so it was deleted.
|
|
63
|
-
|
|
64
|
-
What is proven: the pure semantics (`writeAttributionCapture.test.ts` — explicit wins over the ambient scope, explicit wins over ANOTHER request's scope, "no request" stays "no request", keys omitted rather than `undefined`), and the threading itself (`attributionThreadingParity.test.ts` — every store accepts and uses the carried value, no store still calls a bare `attributionFields()`, every transactional view carries it). The guarantee is structural, and it is stated that way rather than dressed up as a reproduction.
|
|
65
|
-
|
|
66
|
-
Raised by a consumer who could NOT reproduce the loss across 2700 writes at 96-way concurrency with every core saturated, and who asked for the fix anyway on the right grounds: *"impossible beats unlikely when the failure is invisible."*
|
|
67
|
-
- **@voltro/database, @voltro/sql-postgres, @voltro/sql-mysql, @voltro/voltro** — <!-- apiSurface: compatible — `beginLocalWrite`, `endLocalWrite` and `resolveEchoAttribution` are new exports on `@voltro/database`; nothing was removed, renamed or narrowed. Checked rather than assumed, because this repo has already paid once for a narrowing that read as additive. -->
|
|
68
|
-
|
|
69
|
-
Under CDC, a plain `store.insert(...)` could deliver its change with `traceId` and `subjectId` ABSENT — not always, and more often the busier the process.
|
|
70
|
-
|
|
71
|
-
**The registration was racing the echo, and only winning on a margin.** The write path registers the request identity so the transport echo (a postgres NOTIFY, a mysql binlog row) can be re-united with it, and `pendingAttribution.ts` stated the ordering as a guarantee: the registration happens "BEFORE the transport can possibly echo it, because a NOTIFY fires at COMMIT". That is true INSIDE a transaction, where `routeEvent` runs before COMMIT. It is false for a plain write, whose statement commits ITSELF — the trigger fires while the write path is still awaiting the driver, and the registration lands afterwards. The echo has to make a round trip through the LISTEN/binlog connection, and that round trip was the only thing keeping the order right.
|
|
72
|
-
|
|
73
|
-
**Why it stayed hidden.** An unattributed event is a LEGAL event meaning "no request behind this write", so a lost identity is indistinguishable from a background job — there is no error, no warning, and nothing that looks wrong in a trace. It surfaced as one failing assertion in the full test suite, which is the only place this machine is loaded enough to flip the order, and it passed on every isolated re-run.
|
|
74
|
-
|
|
75
|
-
**Closed by construction, not by widening the margin.** A store now brackets each non-transactional write (`beginLocalWrite` / `endLocalWrite`) and delivers echoes through `resolveEchoAttribution`. An echo arriving while a write to that table is mid-registration is HELD until that write has had its chance, then answered. Held echoes keep arrival order — a subscriber seeing an update before the insert it updates would be worse off than one missing a `traceId` — and a write that never registers releases the table after a bound rather than parking its stream.
|
|
76
|
-
|
|
77
|
-
Three narrowings, each deliberate:
|
|
78
|
-
|
|
79
|
-
- **Only under `cdc`.** Nothing is injected in `inline` mode, so there is no echo to order against. - **Only non-transactional writes.** `transactional()` registers before COMMIT already; bracketing it would hold OTHER replicas' echoes for the length of the transaction to fix a race that path does not have. Asserted, so the narrowing is on the record rather than something a later reader "fixes". - **A remote write is still never attributed.** The barrier may delay an answer; it must never invent one. Nothing is registered locally for another replica's write, and that stays true while echoes are held.
|
|
80
|
-
|
|
81
|
-
**Both CDC stores had a hand-written copy of the claim-and-emit tail**, which is the shape of the previous three attribution defects in this package. The decision now lives in one function both call, and `echoBarrierParity.test.ts` fails if either store claims for itself, leaves a public write outside the bracket, or brackets the transactional path. The ordering itself is proven against the primitive (`pendingAttribution.test.ts`) — a live suite cannot force it, because the resume point is the driver's choice, and the four barrier tests were verified to go red against the pre-fix behaviour before being trusted.
|
|
82
|
-
|
|
83
|
-
Also fixed: `mysql`'s `updateMany` / `deleteMany` route their events after their transaction commits, so they carried the same race despite not going through the per-row write path. They are bracketed too.
|
|
84
|
-
- **@voltro/cli, @voltro/voltro** — `voltro build` could fail to build the **start bundle** with `Could not resolve "@voltro/cli/startEntry"`, degrading `voltro start` to the slower per-module boot. The serve bundle carried the identical latent failure.
|
|
85
|
-
|
|
86
|
-
Both bundles generate an entry importing a narrow CLI export and build it with `absWorkingDir: <app root>` — so esbuild resolved that bare specifier from the APP's `node_modules`. Under strict pnpm the app has `@voltro/cli` there only if it DECLARES it, and an app normally depends on `voltro` / `@voltro/web` and gets the CLI transitively. The CLI now resolves its own entry from `import.meta.url` and hands esbuild an absolute alias.
|
|
87
|
-
|
|
88
|
-
Same shape as the tsx bug (`tsxLoader.ts`): a package that is OUR dependency, resolved from the user's directory, invisible under strict pnpm. Same answer — the CLI knows where it lives, so it stops asking the app.
|
|
89
|
-
|
|
90
|
-
**The published export was not the problem.** It is present in the tarball — checked against the real 0.20.1 and 0.20.2 packages, `./startEntry` → `./dist/startEntry.js`, file included. Only resolution failed, which is why re-adding the export would have changed nothing.
|
|
91
|
-
|
|
92
|
-
This monorepo hoists everything, so the bare specifier resolves here and the build passes with or without the alias. The test therefore asserts the alias directly rather than inferring it from a green build — the hoisted layout is exactly what hid the strict-pnpm failure in the first place.
|
|
12
|
+
- **@voltro/cli** — **Two things a QUERY could not do that a mutation beside it could — and a redirect that answered 500 in dev and 303 in production.**
|
|
93
13
|
|
|
94
|
-
|
|
14
|
+
### `useAggregate` in a subscription handler: `Service not found`
|
|
95
15
|
|
|
96
|
-
|
|
16
|
+
The documented way to read an aggregate from a handler (`useAggregate(def).read(...)`) failed at runtime with `Service not found: @voltro/AggregateRegistry`, on every delivery.
|
|
97
17
|
|
|
98
|
-
|
|
99
|
-
- **@voltro/cli, @voltro/voltro** — <!-- apiSurface: compatible — `CliRuntime` keeps its exported signature and its behaviour (NodeContext + logger); only `runCli`'s internal composition changed. `loggerConfig` is module-private. -->
|
|
18
|
+
Both boot paths hand the subscription/query executor a `provideEffect` callback, and each had written its own — smaller — layer set:
|
|
100
19
|
|
|
101
|
-
|
|
20
|
+
| path | provided to an Effect-returning query | |---|---| | `voltro dev` | store + actionBase (**no** `mergedUserLayer`) | | `voltro serve` | store, and nothing else | | either, handler path | the full set |
|
|
102
21
|
|
|
103
|
-
|
|
22
|
+
So a query could not `yield*` the aggregate registry, the cache, the kv store, a plugin's service or the app's own `layers:` — while a mutation in the same app could. `useAggregate` was one symptom of the set being wrong, not a bug of its own.
|
|
104
23
|
|
|
105
|
-
The
|
|
24
|
+
The cost the reporter measured is worth repeating: a subscription whose delivery fails renders NOTHING, so their working-time card showed `00:00` — no error, no empty state. Silence is the worst failure shape a data path has.
|
|
106
25
|
|
|
107
|
-
|
|
26
|
+
Both paths now provide the same set, and `serveApi` reaches it through the single helper its handler path already used (it had two, one complete and one not).
|
|
108
27
|
|
|
109
|
-
|
|
28
|
+
**Why their tests could not see it**, in their words: `@voltro/testing` supplies `aggregateRegistryLayer(...)` and the existing example uses it, so the suite provided exactly what production lacked — *"wenn der Layer im Test nötig ist und in der Laufzeit fehlt, ist er genau der falsche Default."* That is the sharpest line in the report. A harness that hands the code under test something the runtime does not is a second implementation, not a harness. The runtime supplies it now; the layer stays available for tests that genuinely stand alone.
|
|
110
29
|
|
|
111
|
-
|
|
112
|
-
- **@voltro/cli, @voltro/voltro** — Three corrections to the `db drift` baseline shipped in 0.20.2, all reported by the consumer who verified the fix — and all of them defects in that fix rather than in older code.
|
|
30
|
+
### A loader that throws `RedirectError` answers 303 in dev too
|
|
113
31
|
|
|
114
|
-
|
|
32
|
+
`agent-docs/routing.md` promises a 303 with `Location`. `voltro start` did it; `voltro dev` caught the same throw as a render failure and answered **500 with no `Location`**.
|
|
115
33
|
|
|
116
|
-
|
|
34
|
+
The divergence was written down as intended — *"each path maps it onto its own convention"* — and recording it is what let it stand. A redirect is CONTROL FLOW. Two renderers that disagree about that disagree about what the app does, and the one they disagreed on is the one every developer and every dev-environment probe hits first. It cost the reporter a rollout: a readiness probe walked a redirecting route, got the 500, and the deployment never became ready.
|
|
117
35
|
|
|
118
|
-
|
|
36
|
+
One mapper (`loaderControlResponse`) now answers for both, brand-checked rather than `instanceof` because the error crosses a bundle boundary — and since the CLI cannot import `@voltro/web`, a test reads that package's source so a renamed brand cannot silently put dev back to 500.
|
|
119
37
|
|
|
120
|
-
|
|
38
|
+
`NotFoundError` → 404 comes with it, for the same reason.
|
|
39
|
+
- **@voltro/client, @voltro/cli** — **A hot reload no longer takes every page down, and a route that owns the whole origin says so.**
|
|
121
40
|
|
|
122
|
-
|
|
41
|
+
### `defineStore` and module re-evaluation
|
|
123
42
|
|
|
124
|
-
|
|
43
|
+
The duplicate-name guard keyed on the NAME alone and threw. It is right about the danger — two stores sharing a name silently share state — and wrong about one case: a module RE-EVALUATING, which is exactly what a hot reload does.
|
|
125
44
|
|
|
126
|
-
|
|
45
|
+
A consumer measured the cost on one running dev server:
|
|
127
46
|
|
|
128
|
-
|
|
129
|
-
- **@voltro/cli, @voltro/voltro** — `voltro dev` served a client-only shell — with a **200** — whenever a `renderMode: 'ssr'` page failed to render on the server. It now answers 500 with the cause, exactly as `voltro start` does.
|
|
47
|
+
| | status | bytes | |---|---|---| | fresh boot | 200 | 43629 | | touch any `*.store.ts` | 500 | 2328 | | three further requests | 500 | — |
|
|
130
48
|
|
|
131
|
-
|
|
49
|
+
One edit to a store — or to anything importing one — took all 225 of their pages down for the rest of the session, with no self-recovery. Their workaround memoised registrations by name on `globalThis`, which works and costs the thing HMR is for: editing a store's initial state stopped taking effect until a restart.
|
|
132
50
|
|
|
133
|
-
|
|
51
|
+
Registration is keyed by ORIGIN **and evaluation PASS** now — the calling module's stack frame, without `line:column` so that shifting the call down a line is still the same origin. The same module may redefine its own store and gets the LIVE handle back, so state survives the edit. A DIFFERENT module still throws, and the error now names both files, because "rename one" is only actionable if you know which two to look at.
|
|
134
52
|
|
|
135
|
-
|
|
53
|
+
Origin alone was not enough, and the existing suite caught it: two `defineStore('x')` calls in ONE file share an origin, so origin-keying merged them — the exact silent state-sharing the guard exists for. A module body runs synchronously, so two registrations in one file land in the same pass; a hot reload re-evaluates in a LATER tick. Same origin AND same pass is a collision; same origin, later pass is a reload.
|
|
136
54
|
|
|
137
|
-
|
|
55
|
+
(`line:column` would separate those two as well, and was rejected: stripping the position is what lets an edit ABOVE a `defineStore` call shift it down a line without reading as a new origin — and that edit is the common case this is about.)
|
|
138
56
|
|
|
139
|
-
|
|
57
|
+
When the runtime gives no usable stack, it behaves like a collision rather than a redefinition: if the two cannot be told apart, silently sharing state is the worse outcome, and that is what the guard exists for.
|
|
140
58
|
|
|
141
|
-
|
|
59
|
+
### A route whose first segment is dynamic
|
|
142
60
|
|
|
143
|
-
|
|
61
|
+
`voltro doctor` reports a page route like `[id]/[playerCode]` — its FIRST segment dynamic, so it answers every two-segment URL on that origin, `/api/health` included.
|
|
144
62
|
|
|
145
|
-
|
|
63
|
+
The pattern is not a bug; that is what it means. It is invisible until something requests such a URL, and then the page renders, its loader runs, and the failure reads as an application error rather than as a route claiming a path nobody meant it to. Advisory, and scoped so it stays readable: `orders/[id]` is not flagged — a dynamic segment under a literal one is bounded by that literal.
|
|
146
64
|
|
|
147
|
-
-
|
|
65
|
+
The same report described this as REST routes losing to page segments. They do not compete: `restRoutes` are served by the API server and pages by the web server, on different ports. The three-segment probe paths they adopted worked because the pattern is two-segment, not because precedence changed.
|
|
148
66
|
|
|
149
|
-
|
|
67
|
+
### Two items from the same report were already shipped
|
|
150
68
|
|
|
151
|
-
|
|
69
|
+
Both were measured against 0.20.1 and landed in **0.21.0**, so they need no change — only saying so:
|
|
152
70
|
|
|
153
|
-
|
|
71
|
+
- `Could not resolve "@voltro/cli/startEntry"` during the start-bundle build → fixed by `resolve @voltro/cli/{start,serve}Entry from the CLI, not the app root`. It is the same class as the `tsx` bare-specifier bug: a specifier for a package the APP never declared is invisible from the app root under strict pnpm. - `voltro schedule run <name>` exists, with `--process`, `--trigger manual|external` and `--url`. Note it POSTs to a mutating inspect endpoint, so it now needs `VOLTRO_INSPECT_WRITE_TOKEN` outside dev.
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.97.0",
|
|
13
13
|
"@effect/rpc": "^0.76.0",
|
|
14
|
-
"@voltro/ai": "0.
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/protocol": "0.
|
|
19
|
-
"@voltro/runtime": "0.
|
|
14
|
+
"@voltro/ai": "0.22.1",
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/protocol": "0.22.1",
|
|
19
|
+
"@voltro/runtime": "0.22.1",
|
|
20
20
|
"effect": "^3.22.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@voltro/testing": "0.
|
|
23
|
+
"@voltro/testing": "0.22.1",
|
|
24
24
|
"typescript": "^6.0.3",
|
|
25
25
|
"vitest": "^4.1.10"
|
|
26
26
|
}
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-auth": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
21
|
-
"@voltro/sql-postgres": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-auth": "0.22.1",
|
|
19
|
+
"@voltro/protocol": "0.22.1",
|
|
20
|
+
"@voltro/runtime": "0.22.1",
|
|
21
|
+
"@voltro/sql-postgres": "0.22.1",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.
|
|
25
|
+
"@voltro/testing": "0.22.1",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
27
|
"vitest": "^4.1.10"
|
|
28
28
|
}
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
19
|
+
"@voltro/protocol": "0.22.1",
|
|
20
|
+
"@voltro/runtime": "0.22.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.22.1",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-deactivation": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-deactivation": "0.22.1",
|
|
19
|
+
"@voltro/protocol": "0.22.1",
|
|
20
|
+
"@voltro/runtime": "0.22.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.22.1",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@react-email/components": "^1.0.12",
|
|
14
14
|
"@react-email/render": "^1.4.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-mail": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-mail": "0.22.1",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
20
|
+
"@voltro/protocol": "0.22.1",
|
|
21
|
+
"@voltro/runtime": "0.22.1",
|
|
22
22
|
"effect": "^3.22.0",
|
|
23
23
|
"react": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.22.1",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.
|
|
19
|
-
"@voltro/plugin-storage": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
22
|
-
"@voltro/sql-mysql": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
19
|
+
"@voltro/plugin-storage": "0.22.1",
|
|
20
|
+
"@voltro/protocol": "0.22.1",
|
|
21
|
+
"@voltro/runtime": "0.22.1",
|
|
22
|
+
"@voltro/sql-mysql": "0.22.1",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.22.1",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"test": "voltro test"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@voltro/cli": "0.
|
|
14
|
-
"@voltro/database": "0.
|
|
15
|
-
"@voltro/env": "0.
|
|
16
|
-
"@voltro/plugin-multitenancy": "0.
|
|
17
|
-
"@voltro/plugin-storage": "0.
|
|
18
|
-
"@voltro/protocol": "0.
|
|
19
|
-
"@voltro/runtime": "0.
|
|
13
|
+
"@voltro/cli": "0.22.1",
|
|
14
|
+
"@voltro/database": "0.22.1",
|
|
15
|
+
"@voltro/env": "0.22.1",
|
|
16
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
17
|
+
"@voltro/plugin-storage": "0.22.1",
|
|
18
|
+
"@voltro/protocol": "0.22.1",
|
|
19
|
+
"@voltro/runtime": "0.22.1",
|
|
20
20
|
"effect": "^3.22.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@voltro/testing": "0.
|
|
23
|
+
"@voltro/testing": "0.22.1",
|
|
24
24
|
"typescript": "^6.0.3",
|
|
25
25
|
"vitest": "^4.1.10"
|
|
26
26
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.97.0",
|
|
13
13
|
"@effect/rpc": "^0.76.0",
|
|
14
|
-
"@voltro/cli": "0.
|
|
15
|
-
"@voltro/database": "0.
|
|
16
|
-
"@voltro/env": "0.
|
|
17
|
-
"@voltro/plugin-governance": "0.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
14
|
+
"@voltro/cli": "0.22.1",
|
|
15
|
+
"@voltro/database": "0.22.1",
|
|
16
|
+
"@voltro/env": "0.22.1",
|
|
17
|
+
"@voltro/plugin-governance": "0.22.1",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
19
|
+
"@voltro/protocol": "0.22.1",
|
|
20
|
+
"@voltro/runtime": "0.22.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.22.1",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.97.0",
|
|
13
13
|
"@effect/rpc": "^0.76.0",
|
|
14
|
-
"@voltro/cli": "0.
|
|
15
|
-
"@voltro/database": "0.
|
|
16
|
-
"@voltro/env": "0.
|
|
17
|
-
"@voltro/plugin-multitenancy": "0.
|
|
18
|
-
"@voltro/protocol": "0.
|
|
19
|
-
"@voltro/runtime": "0.
|
|
20
|
-
"@voltro/workflow": "0.
|
|
14
|
+
"@voltro/cli": "0.22.1",
|
|
15
|
+
"@voltro/database": "0.22.1",
|
|
16
|
+
"@voltro/env": "0.22.1",
|
|
17
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
18
|
+
"@voltro/protocol": "0.22.1",
|
|
19
|
+
"@voltro/runtime": "0.22.1",
|
|
20
|
+
"@voltro/workflow": "0.22.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.22.1",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-flags": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
22
|
-
"@voltro/sql-postgres": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-flags": "0.22.1",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
20
|
+
"@voltro/protocol": "0.22.1",
|
|
21
|
+
"@voltro/runtime": "0.22.1",
|
|
22
|
+
"@voltro/sql-postgres": "0.22.1",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.22.1",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.97.0",
|
|
14
14
|
"@effect/rpc": "^0.76.0",
|
|
15
|
-
"@voltro/cli": "0.
|
|
16
|
-
"@voltro/database": "0.
|
|
17
|
-
"@voltro/env": "0.
|
|
18
|
-
"@voltro/plugin-audit": "0.
|
|
19
|
-
"@voltro/plugin-governance": "0.
|
|
20
|
-
"@voltro/protocol": "0.
|
|
21
|
-
"@voltro/runtime": "0.
|
|
15
|
+
"@voltro/cli": "0.22.1",
|
|
16
|
+
"@voltro/database": "0.22.1",
|
|
17
|
+
"@voltro/env": "0.22.1",
|
|
18
|
+
"@voltro/plugin-audit": "0.22.1",
|
|
19
|
+
"@voltro/plugin-governance": "0.22.1",
|
|
20
|
+
"@voltro/protocol": "0.22.1",
|
|
21
|
+
"@voltro/runtime": "0.22.1",
|
|
22
22
|
"effect": "^3.22.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.
|
|
25
|
+
"@voltro/testing": "0.22.1",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
27
|
"vitest": "^4.1.10"
|
|
28
28
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.97.0",
|
|
13
13
|
"@effect/rpc": "^0.76.0",
|
|
14
|
-
"@voltro/cli": "0.
|
|
15
|
-
"@voltro/database": "0.
|
|
16
|
-
"@voltro/env": "0.
|
|
17
|
-
"@voltro/kv": "0.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.
|
|
19
|
-
"@voltro/protocol": "0.
|
|
20
|
-
"@voltro/runtime": "0.
|
|
14
|
+
"@voltro/cli": "0.22.1",
|
|
15
|
+
"@voltro/database": "0.22.1",
|
|
16
|
+
"@voltro/env": "0.22.1",
|
|
17
|
+
"@voltro/kv": "0.22.1",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.22.1",
|
|
19
|
+
"@voltro/protocol": "0.22.1",
|
|
20
|
+
"@voltro/runtime": "0.22.1",
|
|
21
21
|
"effect": "^3.22.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.
|
|
24
|
+
"@voltro/testing": "0.22.1",
|
|
25
25
|
"typescript": "^6.0.3",
|
|
26
26
|
"vitest": "^4.1.10"
|
|
27
27
|
}
|