@voltro/cli 0.11.1 → 0.11.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 +40 -0
- package/dist/{apiBuild-D_kEJTxc.js → apiBuild-BVIiATXr.js} +5 -1
- package/dist/apiBuild-C_KY5oY_.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/{commands-BiQsbV50.js → commands-C0ytPFfl.js} +26 -3
- package/dist/index.js +1 -1
- package/dist/{serveCommand-BU454-ay.js → serveCommand-Dd3oLzbo.js} +1 -1
- package/dist/serveEntry.js +1 -1
- package/package.json +17 -17
- package/templates/agent-docs/database/columntypes.md +7 -0
- package/templates/agent-docs/deployment.md +4 -2
- package/templates/agent-docs/internationalization.md +37 -2
- package/templates/agent-docs/testing.md +26 -0
- 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 +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/dist/apiBuild-BGd-BnHq.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,44 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.11.2] — 2026-07-24
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **@voltro/i18n** — Two escapes for adopting typed messages (`createTypedMessages`, #16) app-wide (#19):
|
|
47
|
+
|
|
48
|
+
- **`t.dynamic(runtimeKey, values?)`** — a first-class escape for a genuinely runtime-computed key, on both `useT` and the `useTFn()` result. It takes a plain string with NO forced ICU args, so it doesn't fight the strict literal-key surface. Until now the natural escape — casting a computed key to the catalog key union — made things WORSE: that union spans placeholder-bearing keys, so the call then demanded a spurious 2nd ICU arg. `t.dynamic` is the documented, discoverable alternative. - **`LooseTFunction`** — the widened `(id: string, values?) => string` signature to type a `t` pass-through across a package boundary that can't import the app catalog, instead of falling back to `(...args: any[]) => string`. A strict `TypedTFunction` is deliberately NOT assignable to it (a narrowed key param can't satisfy a wider one — that would erase the checking); pass `t.dynamic` at the boundary, which IS a `LooseTFunction`.
|
|
49
|
+
|
|
50
|
+
Additive: `TypedTFunction<C>` gains a `.dynamic` member (the callable surface is unchanged, so `Parameters<TypedTFunction<C>>[0]` and existing typed call sites still resolve). `createTypedMessages` attaches `.dynamic` in place on the two translate functions — no new per-render closure, so a captured `t`'s identity stays stable.
|
|
51
|
+
- **@voltro/testing, @voltro/database** — `fixtureRow(table, overrides)` (`@voltro/testing`) completes a partial test row so it satisfies the 0.11.1 required-column insert validation — WITHOUT disabling the check. It fills every NOT-NULL, no-default, non-auto-stamped column the payload omits with a schema-typed placeholder (a `oneOf` column takes its first allowed value; a `unique` column gets a distinct value per call so two fixtures don't collide; `timestamp`/`date` get a fixed epoch), then merges your overrides on top (an explicit value always wins). It leaves out exactly what a caller may omit — nullable, defaulted, and framework auto-stamped columns (id / tenant / audit) — and refuses to guess a structured type (`json` / `bytes` / `vector` / `array` / `interval` / `raw`), throwing a message that names the column and says to pass it explicitly.
|
|
52
|
+
|
|
53
|
+
The motivating case: 0.11.1 made the in-memory/test store reject the same partial inserts real Postgres always would (correct — it surfaced a latent prod bug), which turned lean fixtures (`insert(users, { id })`, an omitted required FK) into `TableValidationFailed`. The wrong fix is a `validateInserts: false` knob — it re-hides that bug class, and a test store laxer than production is a fake testing itself. `fixtureRow` is the right one: it makes the fixture COMPLETE.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
await ctx.store.insert('journal_entries', fixtureRow(journalEntries, {
|
|
57
|
+
tenantId, amount: '100.00', // the columns THIS test cares about
|
|
58
|
+
})) // entryNumber, postedAt, … auto-filled + unique
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
It is a runtime filler for the loose `store.insert(name, row)` path (what fixtures use). For COMPILE-time payload typing, use `insertRow` / `upsertRow` from `@voltro/database`. The auto-stamped column set it skips is now exported as `AUTO_FILLED_COLUMNS` from `@voltro/database` — the same list `InferInsertRow` derives its optional columns from, single-sourced so the two can't drift.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **@voltro/cli** — `voltro build` now emits **directly-executable** boot bundles for BOTH app kinds: the web start bundle (`.framework/dist-web/startBundle/startEntry.js`) and the api serve bundle (`.framework/dist-api/serveBundle/serveEntry.js`) each carry a main-guard that boots the app when run as `node <entry>.js`, and stays inert when imported (the `voltro start` / `voltro serve` dev fast paths are unchanged). Production containers can now use `CMD ["node", "…/startEntry.js"]` (or `serveEntry.js`) instead of `pnpm voltro start` / `pnpm voltro serve` — no pnpm process, no `@voltro/cli` bin at runtime — which is what makes `voltro prune-runtime` safe to enable on both: with the self-contained bundle as the real entrypoint, the @vercel/nft trace roots there and legitimately drops `@voltro/cli` and the whole inlined framework tree (a static site's `node_modules` collapses to ~0; a memory api's 146 MB → 11 MB). `prune-runtime` now also roots the trace at the serve bundle. The serve entry chdir's to the app root BEFORE its app-module registry keys are computed from cwd, preserving relocation-safety. Existing `pnpm voltro start` / `pnpm voltro serve` entrypoints keep working. The standalone Dockerfiles gain a build-time boot smoke that fails the build unless the pruned tree reaches ready.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- **@voltro/i18n** — `createTypedMessages` (#16) no longer extracts phantom required vars from a nested plural/select message (#19). For `'{count, plural, one {# day} other {# days total duration}}'`, the type-level `ICUVars` parse was reading a branch's TEXT (`"# days total duration"`) as a bogus required arg name, so `useT('key', { count })` failed to typecheck even though it renders perfectly — and a real var nested inside a branch was dropped. `ICUArgName` now resolves to `never` for any candidate that isn't a valid ICU identifier (`^[A-Za-z0-9_]+$`), so branch text — which contains spaces / `#` / `—` — is never mistaken for a var. Only the top-level arg (`count`) is required, matching what the message actually needs.
|
|
70
|
+
|
|
71
|
+
Scope note: a REAL var nested inside a plural branch (`other {# — {discipline}}`) is still not collected, so it reads as not-required rather than wrongly-required — the safe direction. Apps that pluralise in JS over simple `{count}` messages (the Voltro idiom) were already fully typed and are unaffected.
|
|
72
|
+
- **@voltro/database, @voltro/runtime** — `InferInsertRow` (and thus `insertRow` / `upsertRow`, #15) no longer requires a non-nullable DB-generated (`generatedAs`) column (#20). A stored/virtual generated column declared without `.nullable()` and without a default was typed **required**, but MariaDB/Postgres REJECT an explicit value for a generated column — so the type forced the caller to pass a value the database refuses at runtime. `.generatedAs()` now marks the column optional-for-insert exactly like a `.default()` column (the DB supplies it), so it may be omitted; the whole payload guard on every real column stays intact.
|
|
73
|
+
|
|
74
|
+
Two runtime halves complete it, so the loose `store.insert(name, row)` path agrees: the required-column validation (`missingRequiredColumns`) skips generated columns — omitting one is correct, never a missing-column error — and the store write path now STRIPS any value a caller supplied for a generated column before the INSERT reaches the dialect (tracked on the schema registry as `generatedColumns`), so a value from an untyped insert can't blow up on MariaDB. A generated column is never caller-supplied; the framework and the DB own it end to end.
|
|
75
|
+
|
|
76
|
+
The `.generatedAs()` return type narrows from `this` to `ColumnBuilder<…, true>` (the HasDefault flag) — a purely more-permissive refinement: it only makes the column omittable, so no existing code stops compiling.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
42
80
|
## [0.11.1] — 2026-07-23
|
|
43
81
|
|
|
44
82
|
### Added
|
|
@@ -71,6 +109,8 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
71
109
|
- **@voltro/database, @voltro/runtime** — `store.insert` / `upsert` / `insertIgnore` now raise a clear, typed `TableValidationFailed` naming the column when the payload omits one that is NOT NULL, has no default, and isn't auto-stamped — instead of a raw dialect `SqlError: Failed to execute statement` (`Field '…' doesn't have a default value`) surfaced only on the INSERT path (so it lay dormant until the first row with no existing cache entry). An upsert / insertIgnore whose payload is missing one of its own `conflictColumns` is likewise named at the call (an absent conflict key can't match its target). The check runs AFTER stamping, so auto-id / tenant / audit columns never trip it, and skips nullable, defaulted, and id (`idScheme`) columns — exactly the ones a caller may legitimately omit.
|
|
72
110
|
|
|
73
111
|
Two pure helpers back it — `missingRequiredColumns(table, row)` and `missingConflictColumns(conflictColumns, row)` (exported from `@voltro/database`). This is the runtime half of the "handler data silently disagrees with the schema" class; a compile-time payload type needs the column DSL to track `hasDefault` at the type level, which is a separate change.
|
|
112
|
+
|
|
113
|
+
**Migration impact — behaviour-breaking for lenient test fixtures.** The in-memory/test store now rejects the same partial inserts a real Postgres always would, so it stops being laxer than production — which is the point (it surfaced at least one latent prod bug where a NOT-NULL `text().unique()` column was written without a value). But a fixture that inserted a partial row (`{ id }` parents, an omitted required FK) and passed against the old lenient memory store now throws `TableValidationFailed`. There is no code-level codemod — the fix is fixture DATA: fill the required columns. Use the new `fixtureRow(table, overrides)` helper in `@voltro/testing`, which fills every NOT-NULL-no-default column with a schema-typed placeholder and merges your overrides on top, so a fixture complies without disabling the check. There is deliberately no opt-out to turn the validation off: a test store that accepts rows production rejects is a fake testing itself.
|
|
74
114
|
- **@voltro/database, @voltro/plugin-ai-flows, @voltro/plugin-audit, @voltro/plugin-deactivation, @voltro/plugin-soft-delete** — Compile-time payload typing for writes (#15) — the type-level half that the runtime `TableValidationFailed` guard flagged as a separate change. `insertRow` / `upsertRow` take the TABLE OBJECT (not a string name), so the payload is checked against `InferInsertRow<T>`: every column is required EXCEPT nullable ones, columns with a default, and the framework-filled id/tenant/audit columns. A missing NOT-NULL-no-default column — the exact `lastRefreshedAt` / `teamId` omission from the report — is now a COMPILE error at the call, not a runtime SqlError only on the INSERT path; `upsertRow`'s `conflictColumns` are constrained to the table's own columns too.
|
|
75
115
|
|
|
76
116
|
import { insertRow } from '@voltro/database' await insertRow(ctx.store, roadmapEpicStats, { teamId, lastRefreshedAt: new Date() })
|
|
@@ -153,15 +153,19 @@ const drivers = ${JSON.stringify(t)}\nlet mod\nfor (const d of drivers) {\n try
|
|
|
153
153
|
...c(_) ? { tsconfig: _ } : {}
|
|
154
154
|
}), y = Object.keys(v.metafile.inputs).map((e) => o(t, e)).filter((e) => e.startsWith(`${t}/`) && !e.includes("/node_modules/") && !e.includes("/.framework/") && m(e)), b = await f(t), x = /* @__PURE__ */ new Set(), S = [
|
|
155
155
|
"// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
|
|
156
|
-
"import { join as __join } from 'node:path'",
|
|
156
|
+
"import { join as __join, dirname as __dirname, resolve as __resolve } from 'node:path'",
|
|
157
|
+
"import { pathToFileURL as __pathToFileURL, fileURLToPath as __fileURLToPath } from 'node:url'",
|
|
157
158
|
"import { runServe as __runServe, registerAppModules, registerDriver, loadDotEnv } from '@voltro/cli/serveEntry'",
|
|
158
159
|
...b.map((e, t) => `import * as __drv${t} from ${JSON.stringify(e)}`),
|
|
160
|
+
"const __isMain = Boolean(process.argv[1]) && import.meta.url === __pathToFileURL(process.argv[1]).href",
|
|
161
|
+
"if (__isMain) process.chdir(__resolve(__dirname(__fileURLToPath(import.meta.url)), '..', '..', '..'))",
|
|
159
162
|
"const __root = process.cwd()",
|
|
160
163
|
"registerAppModules({",
|
|
161
164
|
...y.map((e) => ` [__join(__root, ${JSON.stringify(a(t, e))})]: () => import(${JSON.stringify(e)}),`),
|
|
162
165
|
"})",
|
|
163
166
|
...b.map((e, t) => `registerDriver(${JSON.stringify(e)}, __drv${t})`),
|
|
164
167
|
"export const runServe = (args = []) => { loadDotEnv([]); return __runServe(args) }",
|
|
168
|
+
"if (__isMain) runServe(process.argv.slice(2)).then((c) => process.exit(typeof c === 'number' ? c : 0))",
|
|
165
169
|
""
|
|
166
170
|
], w = i(r, "serveEntry.ts");
|
|
167
171
|
return await l.writeFile(w, S.join("\n"), "utf8"), await n.build({
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { bt as e } from "./inspectMetrics-BuBwg1yW.js";
|
|
3
|
-
import { n as t } from "./commands-
|
|
3
|
+
import { n as t } from "./commands-C0ytPFfl.js";
|
|
4
4
|
import { createLogger as n } from "@voltro/logger";
|
|
5
5
|
//#region src/bin.ts
|
|
6
6
|
var r = n({ scope: "voltro:cli" }), i = process.argv.slice(2);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { At as e, D as t, Dt as n, F as r, Ft as i, It as a, K as o, M as s, Mt as c, N as l, Nt as u, Ot as d, P as f, Pt as p, T as m, a as h, b as g, c as _, d as v, f as y, jt as ee, k as b, kt as te, n as ne, o as re, p as ie, s as ae, u as oe, v as se, w as ce, x as le, y as ue } from "./inspectMetrics-BuBwg1yW.js";
|
|
2
2
|
import { B as de, K as fe, L as pe, R as me, V as he, ct as ge, i as _e, l as ve, n as ye, o as be, p as xe, rt as Se, tt as Ce, ut as we, z as Te } from "./dev-zJGsTTNb.js";
|
|
3
3
|
import { a as x, c as Ee, i as De, o as Oe, s as ke } from "./devActivity-1WtIVyHc.js";
|
|
4
|
-
import { i as Ae, n as je, s as Me, t as Ne } from "./apiBuild-
|
|
4
|
+
import { i as Ae, n as je, s as Me, t as Ne } from "./apiBuild-BVIiATXr.js";
|
|
5
5
|
import { t as Pe } from "./start-ChN6PO-c.js";
|
|
6
|
-
import { n as Fe, r as Ie } from "./serveCommand-
|
|
6
|
+
import { n as Fe, r as Ie } from "./serveCommand-Dd3oLzbo.js";
|
|
7
7
|
import { basename as S, dirname as C, isAbsolute as Le, join as w, relative as T, resolve as E, sep as Re } from "node:path";
|
|
8
8
|
import { fileURLToPath as ze, pathToFileURL as Be } from "node:url";
|
|
9
9
|
import { FileSystem as Ve } from "@effect/platform";
|
|
@@ -282,8 +282,25 @@ external scheduler owns the once-only guarantee.
|
|
|
282
282
|
return await N.writeFile(o, [
|
|
283
283
|
"// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
|
|
284
284
|
"import { runStartCommand as __run, loadDotEnv } from '@voltro/cli/startEntry'",
|
|
285
|
+
"import { pathToFileURL, fileURLToPath } from 'node:url'",
|
|
286
|
+
"import { dirname, resolve } from 'node:path'",
|
|
285
287
|
"export const runStartCommand = (args = []) => { loadDotEnv([]); return __run(args) }",
|
|
286
288
|
"export { loadDotEnv }",
|
|
289
|
+
"",
|
|
290
|
+
"// DIRECT-INVOKE ENTRY. `node startEntry.js` is the PRODUCTION container command",
|
|
291
|
+
"// — no `pnpm voltro start` wrapper, so the container never resolves the @voltro/cli",
|
|
292
|
+
"// bin and `voltro prune-runtime` can drop the CLI (and the whole inlined",
|
|
293
|
+
"// effect/@voltro/react tree) from the runtime image: the trace roots at THIS",
|
|
294
|
+
"// self-contained bundle, which is now also the real entrypoint. When this module",
|
|
295
|
+
"// is IMPORTED instead (bin/voltro.mjs's dev fast path) the guard is inert.",
|
|
296
|
+
"if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {",
|
|
297
|
+
" // Relocation-safe: the app root is 3 dirs up from",
|
|
298
|
+
" // .framework/dist-web/startBundle/startEntry.js — chdir there so tryRunStart",
|
|
299
|
+
" // resolves the SSR bundle + appConfig from cwd regardless of the launch dir",
|
|
300
|
+
" // (WORKDIR already sets it in our image; this makes it robust anywhere).",
|
|
301
|
+
" process.chdir(resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..'))",
|
|
302
|
+
" runStartCommand(process.argv.slice(2)).then((c) => process.exit(typeof c === 'number' ? c : 0))",
|
|
303
|
+
"}",
|
|
287
304
|
""
|
|
288
305
|
].join("\n"), "utf8"), await t.build({
|
|
289
306
|
entryPoints: [o],
|
|
@@ -714,7 +731,7 @@ external scheduler owns the once-only guarantee.
|
|
|
714
731
|
let { loadApiConfig: t } = await import("./dev-DH13Ysgs.js"), n = await t(e);
|
|
715
732
|
if (n) {
|
|
716
733
|
B.info("building api app", { app: n.name ?? "(unnamed)" });
|
|
717
|
-
let { runApiBuild: t, runServeBundleBuild: r } = await import("./apiBuild-
|
|
734
|
+
let { runApiBuild: t, runServeBundleBuild: r } = await import("./apiBuild-C_KY5oY_.js");
|
|
718
735
|
await t(e);
|
|
719
736
|
try {
|
|
720
737
|
await r(e);
|
|
@@ -8330,6 +8347,12 @@ image, use \`voltro serverless serve\` from the project root.
|
|
|
8330
8347
|
"dist",
|
|
8331
8348
|
"server",
|
|
8332
8349
|
"appConfig.js"
|
|
8350
|
+
],
|
|
8351
|
+
[
|
|
8352
|
+
".framework",
|
|
8353
|
+
"dist-api",
|
|
8354
|
+
"serveBundle",
|
|
8355
|
+
"serveEntry.js"
|
|
8333
8356
|
]
|
|
8334
8357
|
], sp = ["pg", ...Ne.filter((e) => !e.includes("*"))], cp = async (e) => {
|
|
8335
8358
|
let t = w(e, "node_modules");
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { kt as e, t } from "./inspectMetrics-BuBwg1yW.js";
|
|
2
2
|
import { $ as n, A as r, C as i, D as a, E as o, F as s, G as c, H as l, I as u, J as d, M as f, N as p, O as ee, P as m, Q as h, S as g, T as te, U as ne, W as re, X as _, Y as ie, Z as ae, _ as oe, at as v, b as se, d as ce, et as y, f as b, g as le, h as ue, i as x, it as de, j as S, k as C, m as fe, n as pe, nt as me, o as w, ot as T, q as he, s as ge, st as _e, t as ve, ut as E, v as ye, w as be, x as xe, y as D } from "./dev-zJGsTTNb.js";
|
|
3
3
|
import { a as Se, r as Ce } from "./startupRunner-DhlX9nqd.js";
|
|
4
|
-
import { n as we, s as O } from "./apiBuild-
|
|
4
|
+
import { n as we, s as O } from "./apiBuild-BVIiATXr.js";
|
|
5
5
|
import { t as Te } from "./bootTiming-BdyP9nYw.js";
|
|
6
6
|
import { join as Ee } from "node:path";
|
|
7
7
|
import { pathToFileURL as De } from "node:url";
|
package/dist/serveEntry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { bt as e } from "./inspectMetrics-BuBwg1yW.js";
|
|
2
2
|
import { dt as t } from "./dev-zJGsTTNb.js";
|
|
3
3
|
import { a as n } from "./startupRunner-DhlX9nqd.js";
|
|
4
|
-
import { t as r } from "./serveCommand-
|
|
4
|
+
import { t as r } from "./serveCommand-Dd3oLzbo.js";
|
|
5
5
|
export { e as loadDotEnv, n as registerAppModules, t as registerDriver, r as runServe };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.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.107.0",
|
|
63
63
|
"@effect/sql": "^0.51.1",
|
|
64
64
|
"@effect/workflow": "^0.18.2",
|
|
65
|
-
"@voltro/ai": "0.11.
|
|
66
|
-
"@voltro/cache": "0.11.
|
|
67
|
-
"@voltro/data-transfer": "0.11.
|
|
68
|
-
"@voltro/database": "0.11.
|
|
69
|
-
"@voltro/env": "0.11.
|
|
70
|
-
"@voltro/kv": "0.11.
|
|
71
|
-
"@voltro/logger": "0.11.
|
|
72
|
-
"@voltro/plugin-auth": "0.11.
|
|
73
|
-
"@voltro/plugin-broadcast": "0.11.
|
|
74
|
-
"@voltro/plugin-mail": "0.11.
|
|
75
|
-
"@voltro/plugin-storage": "0.11.
|
|
76
|
-
"@voltro/plugin-webhooks": "0.11.
|
|
77
|
-
"@voltro/protocol": "0.11.
|
|
78
|
-
"@voltro/runtime": "0.11.
|
|
79
|
-
"@voltro/serverless": "0.11.
|
|
80
|
-
"@voltro/workflow": "0.11.
|
|
65
|
+
"@voltro/ai": "0.11.2",
|
|
66
|
+
"@voltro/cache": "0.11.2",
|
|
67
|
+
"@voltro/data-transfer": "0.11.2",
|
|
68
|
+
"@voltro/database": "0.11.2",
|
|
69
|
+
"@voltro/env": "0.11.2",
|
|
70
|
+
"@voltro/kv": "0.11.2",
|
|
71
|
+
"@voltro/logger": "0.11.2",
|
|
72
|
+
"@voltro/plugin-auth": "0.11.2",
|
|
73
|
+
"@voltro/plugin-broadcast": "0.11.2",
|
|
74
|
+
"@voltro/plugin-mail": "0.11.2",
|
|
75
|
+
"@voltro/plugin-storage": "0.11.2",
|
|
76
|
+
"@voltro/plugin-webhooks": "0.11.2",
|
|
77
|
+
"@voltro/protocol": "0.11.2",
|
|
78
|
+
"@voltro/runtime": "0.11.2",
|
|
79
|
+
"@voltro/serverless": "0.11.2",
|
|
80
|
+
"@voltro/workflow": "0.11.2",
|
|
81
81
|
"chokidar": "^5.0.0",
|
|
82
82
|
"ioredis": "^5.11.1",
|
|
83
83
|
"tinyglobby": "^0.2.17",
|
|
@@ -208,6 +208,13 @@ const rows = await ctx.store.query(database.people.descriptor)
|
|
|
208
208
|
rows[0]?.fullName // → 'Mario Lima'
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
**A generated column is never caller-supplied.** It's omitted from the typed insert
|
|
212
|
+
payload — `insertRow(store, people, { firstName, lastName })` compiles with no
|
|
213
|
+
`fullName`, and its type is optional even when the column is NOT nullable and has no
|
|
214
|
+
default (the DB owns the value). If a loose `store.insert` passes one anyway, the
|
|
215
|
+
framework strips it before the INSERT — MariaDB and Postgres both reject an explicit
|
|
216
|
+
value for a generated column, so this keeps that error from reaching the dialect.
|
|
217
|
+
|
|
211
218
|
## STORED vs VIRTUAL
|
|
212
219
|
|
|
213
220
|
`{ stored: true }` (the rare-but-useful case):
|
|
@@ -460,9 +460,11 @@ Building an API app produces the bundle, and `voltro serve` boots from it. In **
|
|
|
460
460
|
|
|
461
461
|
Because production never transpiles, the serve image needs none of the build toolchain. The framework declares `tsx`, `esbuild`, `vite`, and Tailwind as **optional** dependencies of `@voltro/cli`, and the production Dockerfiles isolate the app with `pnpm --prod --no-optional deploy` — which drops that whole tree (and its native binaries) from the image. A serve image ships only what it runs at runtime: your app, the framework, and the one SQL driver you declared.
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
And it shrinks further, the same way a web image does. The serve bundle is directly executable, so the production API container runs it with `node .framework/dist-api/serveBundle/serveEntry.js` — not `pnpm voltro serve` — with no pnpm process and no `@voltro/cli` bin to resolve at boot. That lets `voltro prune-runtime` drop `@voltro/cli` and the whole inlined framework tree from the runtime image too; what stays is the native SQL driver your app actually declared (traced + kept automatically) — a memory-store api's `node_modules` fell from ~146 MB to ~11 MB in a fixture. The serve entry chdir's to the app root before its app-module registry keys are computed, so the pruned, relocated tree still resolves every module.
|
|
464
464
|
|
|
465
|
-
**
|
|
465
|
+
**Web apps get the same treatment.** `voltro build` also precompiles the `voltro start` runtime into a **start bundle** (`.framework/dist-web/startBundle/startEntry.js`, framework inlined) — so a cold web boot loads one artefact instead of resolving the whole framework graph. This collapses the web `modules` phase the same way the serve bundle does (~17× on a small app in practice), which matters most exactly where it hurts: a scale-from-zero container on a fraction of a vCPU. In production the container runs that bundle **directly** — `node .framework/dist-web/startBundle/startEntry.js`, not `pnpm voltro start` — so there is no pnpm process and no `@voltro/cli` bin to resolve at boot; the bundle is a self-contained, relocation-safe entrypoint (its main-guard chdir's to the app root so cwd-based resolution holds anywhere). In development `voltro start` imports the same bundle; if it ever fails to build or load, that path falls back to the ordinary per-module boot — slower, never broken.
|
|
466
|
+
|
|
467
|
+
**And the image itself shrinks — to almost nothing.** Because the SSR bundle, the start bundle, and the precompiled config all inline + tree-shake the framework, *and the production entrypoint is the bundle itself rather than the CLI*, a booted web app needs from `node_modules` only the runtime-external **native** leaves it actually reaches (a SQL driver an ISR or config path touches) — everything else is already compiled into the bundles. `voltro prune-runtime` traces the real reachable set from that entrypoint (`@vercel/nft`, the same tool behind Next.js `output: standalone`) and drops the rest — including `@voltro/cli` and the whole inlined effect/React tree, which nothing at runtime imports any more. It's automatic — a native driver you use is traced and kept, one you don't is dropped, no per-app allow-list — and for a static/SSR marketing site with no native runtime dependency, `node_modules` collapses to **zero** (measured on a fixture: 151 MB → ~0 B): the image is just the node base plus `.framework`. A build-time **boot smoke** then starts the *pruned* tree with the real `node …/startEntry.js` entrypoint and fails the build unless it reaches `start: ready`, so a slimmed image that can't boot never ships. A smaller image is a faster cold pull on a fresh node — the other half of the scale-from-zero latency the boot bundle can't touch.
|
|
466
468
|
|
|
467
469
|
## Tiers (Voltro Cloud — coming soon)
|
|
468
470
|
|
|
@@ -176,9 +176,44 @@ useT('header.search') // ✓ no placeholders → no values arg
|
|
|
176
176
|
useT('does.not.exist') // ✗ compile error — not a catalog key
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
It's pure type refinement — the runtime is the same `useT` / `useTFn` / `<T
|
|
179
|
+
It's almost pure type refinement — the runtime is the same `useT` / `useTFn` / `<T>` (plus a `.dynamic` escape, below), only the signatures narrow to your catalog. Requires the base catalog to be `as const` (so its message strings survive as literal types).
|
|
180
180
|
|
|
181
|
-
**Scope.** Simple `{name}` and single-argument `{count, number}` placeholders are extracted and required.
|
|
181
|
+
**Scope.** Simple `{name}` and single-argument `{count, number}` placeholders are extracted and required. For a *nested* inline ICU message (`{count, plural, one {…} other {…}}` / `select`), the **top-level arg** (`count`) is required and a branch's inner text is NOT mistaken for a var — so `t('duration', { count })` typechecks and renders. A REAL var nested inside a branch (`other {# — {discipline}}`) is not collected, so it reads as *not required* rather than wrongly required; pass it via the loose values bag. Pluralising in JS over simple `{count}` messages (`items.map((i) => t('cart.one', { count: i.n }))`) stays fully typed. `<T>` gets a typed key with loose values, because its rich-text `<tag>` renderers can't be modelled by placeholder extraction.
|
|
182
|
+
|
|
183
|
+
### Runtime-computed keys — `t.dynamic`
|
|
184
|
+
|
|
185
|
+
For a key you build at runtime, do NOT cast it to the catalog key union — that union spans placeholder-bearing keys, so the call then demands a spurious 2nd ICU arg. Use `t.dynamic`, a plain-string escape with no forced args, on both `useT` and the `useTFn()` result:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
const t = useTFn()
|
|
189
|
+
t.dynamic(`status.${row.state}`) // ✓ plain string, no forced arg
|
|
190
|
+
t.dynamic(`greeting.${kind}`, { name }) // values still allowed, but loose
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Passing `t` across a package boundary — `LooseTFunction`
|
|
194
|
+
|
|
195
|
+
Type a `t` pass-through param as `TypedTFunction<C>` to keep it strict — the strict type propagates down through helper signatures. At a boundary that *can't* import your catalog (a shared UI package), type the param `LooseTFunction` (`(id: string, values?) => string`) instead of `(...args: any[]) => string`, and pass `t.dynamic` — which IS a `LooseTFunction`. A strict `TypedTFunction` is deliberately NOT assignable to the loose one (a narrowed key param can't satisfy a wider one, and silently allowing it would erase the checking).
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
// a shared package that can't see the app catalog
|
|
199
|
+
import type { LooseTFunction } from '@voltro/i18n'
|
|
200
|
+
export const formatError = (t: LooseTFunction, e: AppError): string => t(e.messageKey)
|
|
201
|
+
|
|
202
|
+
// in the app, at the boundary:
|
|
203
|
+
formatError(t.dynamic, err)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
A convenient app-wide barrel captures the bound types once:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
// src/i18n.ts
|
|
210
|
+
import { createTypedMessages, type TypedTFunction } from '@voltro/i18n'
|
|
211
|
+
import type en from './locales/en'
|
|
212
|
+
|
|
213
|
+
export const { useT, useTFn, T } = createTypedMessages<typeof en>()
|
|
214
|
+
export type AppTFunction = TypedTFunction<typeof en> // for helper / prop params
|
|
215
|
+
export type AppMessageKey = Parameters<AppTFunction>[0] // the catalog's key union
|
|
216
|
+
```
|
|
182
217
|
|
|
183
218
|
## Reading a message outside React — `meta({ locale })`
|
|
184
219
|
|
|
@@ -320,6 +320,32 @@ Retries are always on and carry no backoff here. Production's jitter exists to d
|
|
|
320
320
|
- **Transport concerns** — connection info, rate limiting, the tenant header. Those are properties of the HTTP hop, not of the procedure; faking them here would only assert against the fake.
|
|
321
321
|
- **Undo capture and the metrics sample** — optional injections the serve entrypoint makes. Their absence changes nothing a handler can observe.
|
|
322
322
|
|
|
323
|
+
## Completing a fixture row — `fixtureRow`
|
|
324
|
+
|
|
325
|
+
`ctx.store` is the real mixin-wrapped store, so `store.insert` runs the same required-column validation production does: a payload that omits a NOT-NULL, no-default, non-auto-stamped column throws `TableValidationFailed` naming the column. That is deliberate — the test store is not laxer than Postgres, so a fixture can't pass on a row the database would reject. But it means a lean fixture (`insert('journal_entries', { id })`, an omitted required FK) now fails.
|
|
326
|
+
|
|
327
|
+
`fixtureRow(table, overrides)` completes the row for you. It fills every column the validator would flag with a schema-typed placeholder, then merges your overrides on top — so you write only the columns the test cares about and the rest are made valid:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import { fixtureRow, makeTestContext } from '@voltro/testing'
|
|
331
|
+
import { journalEntries } from '../schema/journal'
|
|
332
|
+
|
|
333
|
+
const ctx = makeTestContext({ subject, store: mockStore({ journal_entries: [] }) })
|
|
334
|
+
|
|
335
|
+
await ctx.store.insert('journal_entries', fixtureRow(journalEntries, {
|
|
336
|
+
amount: '100.00', // the columns THIS test asserts on
|
|
337
|
+
})) // entryNumber (unique), postedAt, … auto-filled
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
What it fills and what it leaves alone:
|
|
341
|
+
|
|
342
|
+
- **Fills** each required column with a value of the right shape — a `oneOf` column takes its first allowed value, a `unique` column gets a distinct value per call (so two fixtures don't collide on the key), `timestamp` / `date` get a fixed epoch, `decimal` / `bigint` a numeric string.
|
|
343
|
+
- **Leaves out** exactly what a caller may legitimately omit: nullable columns, defaulted columns, and the framework auto-stamped set (`id`, `tenantId`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`, `deletedAt`, `deletedBy`) — the store stamps those from the subject and the table scheme.
|
|
344
|
+
- **Refuses to guess** a structured type (`json`, `bytes`, `vector`, `array`, `interval`, `raw`): it throws naming the column and telling you to pass it explicitly, rather than inventing a value that fails the codec obscurely.
|
|
345
|
+
- **Your override always wins** — including an explicit `null`. Don't assert on a synthesized placeholder; override anything the test inspects.
|
|
346
|
+
|
|
347
|
+
There is no flag to turn the validation off — a test store that accepts rows production rejects is a fake testing itself. `fixtureRow` is a runtime filler for the loose `store.insert(name, row)` path. For **compile-time** payload typing (a missing required column caught as a type error at the call), use [`insertRow` / `upsertRow`](/docs/data/mutations) from `@voltro/database`, which check against `InferInsertRow<T>`.
|
|
348
|
+
|
|
323
349
|
## Subject + tenant re-scoping
|
|
324
350
|
|
|
325
351
|
`withSubject` and `withTenant` re-scope to a different principal for one block, sharing the **same** underlying data — so cross-subject reads exercise real tenant scoping, not a closure stub. This is how you prove isolation: write as one tenant, then assert another tenant can't see the row.
|
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.96.1",
|
|
13
13
|
"@effect/rpc": "^0.75.1",
|
|
14
|
-
"@voltro/ai": "0.11.
|
|
15
|
-
"@voltro/cli": "0.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/protocol": "0.11.
|
|
19
|
-
"@voltro/runtime": "0.11.
|
|
14
|
+
"@voltro/ai": "0.11.2",
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/protocol": "0.11.2",
|
|
19
|
+
"@voltro/runtime": "0.11.2",
|
|
20
20
|
"effect": "^3.21.2"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@voltro/testing": "0.11.
|
|
23
|
+
"@voltro/testing": "0.11.2",
|
|
24
24
|
"typescript": "^5.7.0",
|
|
25
25
|
"vitest": "^3.0.0"
|
|
26
26
|
}
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.96.1",
|
|
14
14
|
"@effect/rpc": "^0.75.1",
|
|
15
|
-
"@voltro/cli": "0.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/plugin-auth": "0.11.
|
|
19
|
-
"@voltro/protocol": "0.11.
|
|
20
|
-
"@voltro/runtime": "0.11.
|
|
21
|
-
"@voltro/sql-postgres": "0.11.
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/plugin-auth": "0.11.2",
|
|
19
|
+
"@voltro/protocol": "0.11.2",
|
|
20
|
+
"@voltro/runtime": "0.11.2",
|
|
21
|
+
"@voltro/sql-postgres": "0.11.2",
|
|
22
22
|
"effect": "^3.21.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@voltro/testing": "0.11.
|
|
25
|
+
"@voltro/testing": "0.11.2",
|
|
26
26
|
"typescript": "^5.7.0",
|
|
27
27
|
"vitest": "^3.0.0"
|
|
28
28
|
}
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.96.1",
|
|
14
14
|
"@effect/rpc": "^0.75.1",
|
|
15
|
-
"@voltro/cli": "0.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
19
|
-
"@voltro/protocol": "0.11.
|
|
20
|
-
"@voltro/runtime": "0.11.
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
19
|
+
"@voltro/protocol": "0.11.2",
|
|
20
|
+
"@voltro/runtime": "0.11.2",
|
|
21
21
|
"effect": "^3.21.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.11.
|
|
24
|
+
"@voltro/testing": "0.11.2",
|
|
25
25
|
"typescript": "^5.7.0",
|
|
26
26
|
"vitest": "^3.0.0"
|
|
27
27
|
}
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.96.1",
|
|
14
14
|
"@effect/rpc": "^0.75.1",
|
|
15
|
-
"@voltro/cli": "0.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/plugin-deactivation": "0.11.
|
|
19
|
-
"@voltro/protocol": "0.11.
|
|
20
|
-
"@voltro/runtime": "0.11.
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/plugin-deactivation": "0.11.2",
|
|
19
|
+
"@voltro/protocol": "0.11.2",
|
|
20
|
+
"@voltro/runtime": "0.11.2",
|
|
21
21
|
"effect": "^3.21.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.11.
|
|
24
|
+
"@voltro/testing": "0.11.2",
|
|
25
25
|
"typescript": "^5.7.0",
|
|
26
26
|
"vitest": "^3.0.0"
|
|
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.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/plugin-mail": "0.11.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
20
|
-
"@voltro/protocol": "0.11.
|
|
21
|
-
"@voltro/runtime": "0.11.
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/plugin-mail": "0.11.2",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
20
|
+
"@voltro/protocol": "0.11.2",
|
|
21
|
+
"@voltro/runtime": "0.11.2",
|
|
22
22
|
"effect": "^3.21.2",
|
|
23
23
|
"react": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.11.
|
|
26
|
+
"@voltro/testing": "0.11.2",
|
|
27
27
|
"typescript": "^5.7.0",
|
|
28
28
|
"vitest": "^3.0.0"
|
|
29
29
|
}
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@effect/platform": "^0.96.1",
|
|
14
14
|
"@effect/rpc": "^0.75.1",
|
|
15
|
-
"@voltro/cli": "0.11.
|
|
16
|
-
"@voltro/database": "0.11.
|
|
17
|
-
"@voltro/env": "0.11.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
19
|
-
"@voltro/plugin-storage": "0.11.
|
|
20
|
-
"@voltro/protocol": "0.11.
|
|
21
|
-
"@voltro/runtime": "0.11.
|
|
22
|
-
"@voltro/sql-mysql": "0.11.
|
|
15
|
+
"@voltro/cli": "0.11.2",
|
|
16
|
+
"@voltro/database": "0.11.2",
|
|
17
|
+
"@voltro/env": "0.11.2",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
19
|
+
"@voltro/plugin-storage": "0.11.2",
|
|
20
|
+
"@voltro/protocol": "0.11.2",
|
|
21
|
+
"@voltro/runtime": "0.11.2",
|
|
22
|
+
"@voltro/sql-mysql": "0.11.2",
|
|
23
23
|
"effect": "^3.21.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.11.
|
|
26
|
+
"@voltro/testing": "0.11.2",
|
|
27
27
|
"typescript": "^5.7.0",
|
|
28
28
|
"vitest": "^3.0.0"
|
|
29
29
|
}
|
|
@@ -10,17 +10,17 @@
|
|
|
10
10
|
"test": "voltro test"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@voltro/cli": "0.11.
|
|
14
|
-
"@voltro/database": "0.11.
|
|
15
|
-
"@voltro/env": "0.11.
|
|
16
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
17
|
-
"@voltro/plugin-storage": "0.11.
|
|
18
|
-
"@voltro/protocol": "0.11.
|
|
19
|
-
"@voltro/runtime": "0.11.
|
|
13
|
+
"@voltro/cli": "0.11.2",
|
|
14
|
+
"@voltro/database": "0.11.2",
|
|
15
|
+
"@voltro/env": "0.11.2",
|
|
16
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
17
|
+
"@voltro/plugin-storage": "0.11.2",
|
|
18
|
+
"@voltro/protocol": "0.11.2",
|
|
19
|
+
"@voltro/runtime": "0.11.2",
|
|
20
20
|
"effect": "^3.21.2"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@voltro/testing": "0.11.
|
|
23
|
+
"@voltro/testing": "0.11.2",
|
|
24
24
|
"typescript": "^5.7.0",
|
|
25
25
|
"vitest": "^3.0.0"
|
|
26
26
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.96.1",
|
|
13
13
|
"@effect/rpc": "^0.75.1",
|
|
14
|
-
"@voltro/cli": "0.11.
|
|
15
|
-
"@voltro/database": "0.11.
|
|
16
|
-
"@voltro/env": "0.11.
|
|
17
|
-
"@voltro/plugin-governance": "0.11.
|
|
18
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
19
|
-
"@voltro/protocol": "0.11.
|
|
20
|
-
"@voltro/runtime": "0.11.
|
|
14
|
+
"@voltro/cli": "0.11.2",
|
|
15
|
+
"@voltro/database": "0.11.2",
|
|
16
|
+
"@voltro/env": "0.11.2",
|
|
17
|
+
"@voltro/plugin-governance": "0.11.2",
|
|
18
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
19
|
+
"@voltro/protocol": "0.11.2",
|
|
20
|
+
"@voltro/runtime": "0.11.2",
|
|
21
21
|
"effect": "^3.21.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.11.
|
|
24
|
+
"@voltro/testing": "0.11.2",
|
|
25
25
|
"typescript": "^5.7.0",
|
|
26
26
|
"vitest": "^3.0.0"
|
|
27
27
|
}
|
|
@@ -11,17 +11,17 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@effect/platform": "^0.96.1",
|
|
13
13
|
"@effect/rpc": "^0.75.1",
|
|
14
|
-
"@voltro/cli": "0.11.
|
|
15
|
-
"@voltro/database": "0.11.
|
|
16
|
-
"@voltro/env": "0.11.
|
|
17
|
-
"@voltro/plugin-multitenancy": "0.11.
|
|
18
|
-
"@voltro/protocol": "0.11.
|
|
19
|
-
"@voltro/runtime": "0.11.
|
|
20
|
-
"@voltro/workflow": "0.11.
|
|
14
|
+
"@voltro/cli": "0.11.2",
|
|
15
|
+
"@voltro/database": "0.11.2",
|
|
16
|
+
"@voltro/env": "0.11.2",
|
|
17
|
+
"@voltro/plugin-multitenancy": "0.11.2",
|
|
18
|
+
"@voltro/protocol": "0.11.2",
|
|
19
|
+
"@voltro/runtime": "0.11.2",
|
|
20
|
+
"@voltro/workflow": "0.11.2",
|
|
21
21
|
"effect": "^3.21.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@voltro/testing": "0.11.
|
|
24
|
+
"@voltro/testing": "0.11.2",
|
|
25
25
|
"typescript": "^5.7.0",
|
|
26
26
|
"vitest": "^3.0.0"
|
|
27
27
|
}
|