@voltro/cli 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/THIRD-PARTY-NOTICES.md +2 -2
  3. package/dist/apiBuild-Ba4njm9m.js +2 -0
  4. package/dist/{apiBuild-deIjJKJm.js → apiBuild-eInHm_bJ.js} +2 -2
  5. package/dist/bin.js +2 -2
  6. package/dist/{commands-BHdc71DG.js → commands-Cxs97AFp.js} +1897 -1664
  7. package/dist/{dev-Bqyq62Ak.js → dev-63Ehpw2p.js} +475 -475
  8. package/dist/dev-CXsR8Zgo.js +2 -0
  9. package/dist/index.js +1 -1
  10. package/dist/{inspectMetrics-BLUBAWLb.js → inspectMetrics-DVCKKSPk.js} +819 -510
  11. package/dist/{serveCommand-Dn6--0Bv.js → serveCommand-Cq7BhtpB.js} +3 -3
  12. package/dist/serveEntry.js +3 -3
  13. package/dist/{start-CWZW_4Et.js → start-CYe-7ueh.js} +371 -306
  14. package/dist/startEntry.js +2 -2
  15. package/package.json +18 -17
  16. package/templates/AGENTS.core.md +15 -4
  17. package/templates/AGENTS.md +15 -4
  18. package/templates/agent-docs/authentication.md +7 -4
  19. package/templates/agent-docs/cli.md +84 -5
  20. package/templates/agent-docs/data.md +94 -70
  21. package/templates/agent-docs/database/schema.md +1 -1
  22. package/templates/agent-docs/reference.md +20 -0
  23. package/templates/agent-docs/routing.md +183 -25
  24. package/templates/agent-docs/testing.md +24 -0
  25. package/templates/apps/api-ai/package.json +7 -7
  26. package/templates/apps/api-auth/package.json +8 -8
  27. package/templates/apps/api-backend/package.json +7 -7
  28. package/templates/apps/api-backend-deactivation/package.json +7 -7
  29. package/templates/apps/api-backend-mail/package.json +8 -8
  30. package/templates/apps/api-backend-mariadb/package.json +9 -9
  31. package/templates/apps/api-backend-storage/package.json +8 -8
  32. package/templates/apps/api-data-advanced/package.json +8 -8
  33. package/templates/apps/api-durable/package.json +8 -8
  34. package/templates/apps/api-feature-flags/package.json +9 -9
  35. package/templates/apps/api-governance/package.json +8 -8
  36. package/templates/apps/api-kv/package.json +8 -8
  37. package/templates/apps/api-moderation/package.json +8 -8
  38. package/templates/apps/api-observability/package.json +8 -8
  39. package/templates/apps/api-ratelimit/package.json +8 -8
  40. package/templates/apps/api-rbac/package.json +8 -8
  41. package/templates/apps/api-rest/package.json +7 -7
  42. package/templates/apps/api-saas/package.json +11 -11
  43. package/templates/apps/api-search/package.json +8 -8
  44. package/templates/apps/api-versioning/package.json +8 -8
  45. package/templates/apps/api-webhooks/package.json +8 -8
  46. package/templates/apps/changelog/package.json +6 -6
  47. package/templates/apps/edge-functions/package.json +2 -2
  48. package/templates/apps/frontend-admin/package.json +8 -8
  49. package/templates/apps/frontend-app/package.json +8 -8
  50. package/templates/apps/frontend-blank/package.json +7 -7
  51. package/templates/apps/frontend-contact/package.json +7 -7
  52. package/templates/apps/frontend-dashboard/package.json +7 -7
  53. package/templates/apps/frontend-docs/package.json +7 -7
  54. package/templates/apps/frontend-i18n/package.json +6 -6
  55. package/templates/apps/frontend-landing/package.json +7 -7
  56. package/templates/apps/frontend-spa/package.json +7 -7
  57. package/templates/apps/frontend-ssr/package.json +7 -7
  58. package/templates/apps/frontend-ssr-api/package.json +8 -8
  59. package/templates/apps/frontend-static-blog/package.json +6 -6
  60. package/dist/apiBuild-CSM74oEZ.js +0 -2
  61. package/dist/dev-BYfrYwrT.js +0 -2
@@ -537,29 +537,39 @@ Cost: every request triggers a fresh render. For very high-traffic pages, prefer
537
537
 
538
538
  ### Streaming SSR
539
539
 
540
- By default an `ssr` page renders with `renderToString` — the whole document is built in memory, then sent. The server-side SSR helpers in `@voltro/web/ssr` also expose a **streaming** path, `renderPageToStream`, built on React's `renderToPipeableStream`. It flushes the shell (and each `<Suspense>` boundary as it resolves) instead of buffering the whole page, so the browser gets the first bytes sooner:
541
-
542
- ```ts
543
- import { renderPageToStream } from '@voltro/web/ssr'
544
-
545
- const { stream, meta } = renderPageToStream({
546
- descriptor,
547
- params,
548
- pathname,
549
- loaderData,
550
- bootstrapModules: ['/assets/client.js'],
551
- onShellReady: () => {
552
- // meta is resolved synchronously write <head> before piping the body.
553
- res.setHeader('content-type', 'text/html')
554
- res.write(`<!doctype html><html><head>${renderMetaToHtml(meta)}</head><body><div id="root">`)
555
- stream.pipe(res)
556
- },
557
- onAllReady: () => res.end('</div></body></html>'),
558
- onShellError: (err) => { res.statusCode = 500; res.end('Internal error') },
559
- })
560
- ```
561
-
562
- `renderPageToStream` takes the same options as `renderPageToHtml` plus the stream callbacks (`onShellReady` / `onAllReady` / `onShellError`) and `bootstrapModules`. Both paths compose the same providers and resolve `meta` synchronously, so the sync string render and the streaming render produce the same tree — the difference is time-to-first-byte and native Suspense streaming. Most apps never call these directly; the `voltro start` pipeline uses them under the hood.
540
+ An `ssr` page is **streamed**, on both boot paths (`voltro dev` and `voltro
541
+ start`). The server sends the `<head>` and the page shell as soon as they are
542
+ rendered, then flushes each `<Suspense>` boundary as its data settles, inside
543
+ the same response.
544
+
545
+ For a page with no deferred data this is a time-to-first-byte win and nothing
546
+ else — React's render loop still runs to completion in one pass, so a slow
547
+ render is still a slow render. The lever that matters is `defer()`: it puts a
548
+ real `<Suspense>` boundary in the tree, which is what lets the server return to
549
+ the event loop while a slow value is still pending. See
550
+ [Deferring slow data](/docs/routing/loaders-and-meta#deferring-slow-data-defer--await).
551
+
552
+ Measured against a real server rendering a page with one 400ms deferred field:
553
+ first body byte at 7ms, the deferred chunk at 408ms — and a probe firing every
554
+ 10ms for the duration of that request was served 30 times with a 3ms median and
555
+ a 7ms maximum. The request does not occupy the event loop while it waits.
556
+
557
+ Two consequences worth knowing:
558
+
559
+ - **The entry script is emitted as a bootstrap module.** A plain
560
+ `<script type="module">` is deferred until the document finishes parsing,
561
+ which on a streamed response is *after the last deferred boundary* — the
562
+ framework hands the URL to React instead, so it goes out `async` at the end
563
+ of the shell and hydration starts immediately.
564
+ - **`isr` and `static` are still buffered**, because both produce a stored
565
+ artefact rather than a response. That is also why `defer()` is an error on
566
+ those modes.
567
+
568
+ Apps do not call the renderer directly. If you are building your own server on
569
+ top of `@voltro/web/ssr`, `renderPageToStream` is the entry point — it takes the
570
+ same options as `renderPageToHtml` plus `bootstrapModules` and the stream
571
+ callbacks, resolves `meta` synchronously so the `<head>` can go out first, and
572
+ returns a Node pipeable stream.
563
573
 
564
574
  ## isr (incremental static regeneration)
565
575
 
@@ -615,6 +625,20 @@ The framework reads Postgres logical replication; writes to `posts` or `comments
615
625
 
616
626
  Requires `SSR_CACHE=postgres` and a `wal_level=logical` Postgres.
617
627
 
628
+ ## spa (client-only, with an optional SSR layout shell)
629
+
630
+ ```tsx
631
+ export const renderMode = 'spa' as const
632
+ ```
633
+
634
+ A `spa` page renders entirely in the browser — the page itself is never server-rendered. Reach for it when a page genuinely needs a fresh client render every load (most reactive dashboards) and doesn't need its own first-paint HTML or SEO.
635
+
636
+ **If the page's route has a layout, that layout is still server-rendered.** The server renders the layout chain — running its layout loaders — around an empty page slot (`<div data-voltro-page-slot>`), inlines the layout data, and marks the page client-only. The browser hydrates that shell and mounts the page into the slot after hydration. So the shell (nav, sidebar, auth gate) gets an instant first paint while the page stays client-only. The page's own `loader` still runs in the browser.
637
+
638
+ Because a layout now runs on the server for spa routes too, a layout used **only** by spa pages must be SSR-safe — no unguarded `window` / `document` in its render or its `loader`. Layouts shared with any `static` / `ssr` / `isr` page already render server-side (and `static` is the default), so they are unaffected. A spa page with **no** layout is a pure client mount, unchanged.
639
+
640
+ > **Deferred (follow-ups, not yet shipped):** `voltro build` does not prerender the spa layout shell to a file — `voltro start` renders it on demand. `defer()` / streaming inside a spa-shell layout is unsupported (the shell is buffered). Fast Refresh of the shell path is not yet specialised.
641
+
618
642
  ## Picking between them
619
643
 
620
644
  | You have… | Use |
@@ -653,14 +677,15 @@ A request to `/foo`:
653
677
  1. **Pre-rendered HTML exists at `dist/foo/index.html`?** Serve it. (static + isr-already-cached.)
654
678
  2. **No pre-render, page is `ssr`?** Render fresh, serve.
655
679
  3. **No pre-render, page is `isr`?** Cache lookup → MISS → render → store → serve.
656
- 4. **No pre-render, page is `static`?** Serve the SPA shell the client router takes over.
680
+ 4. **No pre-render, page is `spa` with a layout?** Render the SSR layout shell (layouts + an empty page slot) on demand; the client mounts the page into the slot.
681
+ 5. **No pre-render, page is `spa` with no layout, or `static`?** Serve the SPA shell — the client router takes over.
657
682
 
658
683
  That last case is how dynamic `static` routes work in dev / when `getStaticPaths` didn't include the URL.
659
684
 
660
685
  ## What doesn't work
661
686
 
662
687
  - **Switching `renderMode` per request.** It's a static module export — one value per build.
663
- - **`renderMode: 'ssr'` without `voltro start`.** SSR requires the production server. Dev mode runs everything client-side regardless.
688
+ - **Assuming `ssr` is client-only in dev.** It is not: `voltro dev` runs the same SSR path `voltro start` does, streaming included, so cookie-driven gates and `useServerRequest()` behave the same in both. What dev does NOT do is pre-render `static` pages — those fall through to the SPA shell.
664
689
  - **`isr` with `cacheInvalidatesOn` against memory cache.** Memory cache is per-process; CDC events fire across processes. Use `SSR_CACHE=postgres`.
665
690
 
666
691
  ## Where to read next
@@ -730,6 +755,130 @@ export default function NotePage(): ReactNode {
730
755
 
731
756
  For `static` pages with `getStaticPaths`, the loader runs once per enumerated path.
732
757
 
758
+ ### Server-rendered data reaches the first client render
759
+
760
+ For `static`, `ssr` and `isr` pages the framework inlines the loader's result
761
+ into the HTML document (a `<script type="application/json"
762
+ id="__voltro_state__">` tag) and the browser adopts it before React hydrates.
763
+ Two consequences worth designing around:
764
+
765
+ - **`useLoaderData()` returns real data on the very first client render.** It is
766
+ not `undefined` until an effect has run, so a page can dereference its loader
767
+ data directly (`data.title`) without a guard, and a layout renders its
768
+ loader's value identically on the server and on the client — no hydration
769
+ mismatch, no flash of fallback content.
770
+ - **The loader does NOT re-run on that initial hydration.** It already ran on
771
+ the server; running it again in the browser would just re-fetch what the page
772
+ is already showing. If you need work to happen after mount (refreshing data,
773
+ a side effect), put it in an effect or use `useSubscription` — do not rely on
774
+ the loader firing a second time.
775
+
776
+ Client-side navigation is unchanged: moving to another route runs that route's
777
+ loaders in the browser as usual. A `spa` page has no server render, so its
778
+ loader runs on the client on first mount.
779
+
780
+ Layout loaders are inlined the same way, keyed per layout, so each layout reads
781
+ its OWN data on the first render. The one exception is `voltro build`'s static
782
+ prerender, which runs page loaders but not layout loaders — a `static` page's
783
+ layouts resolve their data on the client after mount. The PAGE keeps its own
784
+ inlined data for the whole of that window, however slow those layout loaders
785
+ are, so the no-guard promise above holds on a prerendered page too: only the
786
+ layout shows its no-data fallback until its loader settles.
787
+
788
+
789
+ ## Deferring slow data: `defer()` + `<Await>`
790
+
791
+ A loader blocks the whole response. One slow field therefore costs every byte
792
+ of the page — the user stares at nothing while a report query runs. `defer()`
793
+ splits the loader's result into data that blocks the shell and data that
794
+ **streams in after it**, behind a `<Suspense>` boundary the server flushes as
795
+ soon as the promise settles.
796
+
797
+ ```tsx
798
+ // src/pages/dashboard.tsx
799
+ import { Await, defer, useLoaderData } from '@voltro/web'
800
+
801
+ export const renderMode = 'ssr' as const // required — see below
802
+
803
+ export const loader = async ({ query }: { query?: <T>(tag: string, input?: Record<string, unknown>) => Promise<T> }) => defer(
804
+ // EAGER — awaited before the shell renders. Keep this fast.
805
+ { user: await query?.<User>('users.me') },
806
+ // DEFERRED — NOT awaited. Each becomes a promise on useLoaderData().
807
+ { report: query!<QuarterlyReport>('reports.quarterly') },
808
+ )
809
+
810
+ export default function Dashboard() {
811
+ const { user, report } = useLoaderData<Awaited<ReturnType<typeof loader>>>()
812
+ return (
813
+ <main>
814
+ <h1>Hello {user?.name}</h1>
815
+
816
+ <Await value={report} fallback={<ReportSkeleton />}>
817
+ {(report) => <ReportTable rows={report.rows} />}
818
+ </Await>
819
+ </main>
820
+ )
821
+ }
822
+ ```
823
+
824
+ What the browser sees: the full page with `<ReportSkeleton />` in place,
825
+ immediately — then the real table, injected in a later chunk of the same
826
+ response. No second request, no client-side fetch, no loading spinner driven by
827
+ `useEffect`.
828
+
829
+ **Two explicit buckets, not one object.** `defer(eager, deferred)` takes them
830
+ separately rather than treating any promise-valued field as deferred. Deferral
831
+ is then something you wrote down, not something inferred from a value's runtime
832
+ shape — and `useLoaderData()` can type it: eager fields come back as values,
833
+ deferred fields as `Promise<T>`, so the compiler tells you which ones need an
834
+ `<Await>`.
835
+
836
+ ### `<Await>`
837
+
838
+ | Prop | Meaning |
839
+ |---|---|
840
+ | `value` | A deferred field off `useLoaderData()`. |
841
+ | `fallback` | Rendered until the value arrives. **This is what ships in the streamed shell** — keep it cheap and layout-stable. |
842
+ | `children` | `(value) => ReactNode` — rendered with the resolved value. |
843
+ | `errorFallback` | Rendered if the deferred promise rejects. Without it, a rejection renders nothing in that subtree. |
844
+
845
+ `<Await>` owns the `<Suspense>` boundary and the hydration handoff for the
846
+ streamed value. Do not hand-roll it with `<Suspense>` + `use()` — the server
847
+ and the client would then have to agree on a wire format that the framework
848
+ otherwise guarantees by construction.
849
+
850
+ A rejected deferred value never takes the page down: it renders
851
+ `errorFallback` in place, on the server and on the client alike.
852
+
853
+ ### `defer()` requires `renderMode: 'ssr'` and full interactivity
854
+
855
+ Every other combination is a **hard error at boot or build**, naming the page —
856
+ because each one fails silently otherwise:
857
+
858
+ | Combination | Why it is rejected |
859
+ |---|---|
860
+ | `renderMode: 'static'` | The static prerender uses `renderToString`, which does not support Suspense. It emits an errored boundary and a "switched to client rendering" template with no warning — the artefact would ship a permanent fallback. |
861
+ | `renderMode: 'isr'` | ISR caches a completed HTML string. Filling it in would make `defer()` a silent no-op that still reads like it streams. |
862
+ | `interactive: 'none'` | Revealing a streamed boundary needs React's inline reveal scripts, and this mode ships no JS. The fallback would be permanent. |
863
+ | `interactive: 'islands'` | The page's React root never hydrates, so nothing consumes the streamed value. |
864
+
865
+ In all four cases the fix is the same: put the value in the eager bucket (or
866
+ return it directly) and let the page render as it did before.
867
+
868
+ ### Layout loaders can defer too
869
+
870
+ A `layout.tsx` loader may return `defer()` under the same rules. Its deferred
871
+ fields are keyed per layout, so a layout reads its own promises via
872
+ `useLoaderData()` exactly as a page does.
873
+
874
+ ### Client-side navigation
875
+
876
+ On a client-side navigation there is no server render, so the loader runs in the
877
+ browser and its deferred fields are ordinary promises. `<Await>` renders the
878
+ fallback and swaps in the content when they settle — the same code, driven by
879
+ React alone.
880
+
881
+
733
882
  ## Loader arguments
734
883
 
735
884
  ```ts
@@ -797,6 +946,15 @@ Two rules that fall out of this:
797
946
 
798
947
  Under the hood, `ctx.query` is a one-shot `POST /rpc` call (see [Wire protocol](/docs/data/wire-protocol#http-one-shot-rpc-post-rpc)).
799
948
 
949
+ ### Authentication in loaders
950
+
951
+ Auth is resolved by the **api**, never the web app. Two rules follow:
952
+
953
+ - **Strategies live on the `type:'api'` app.** A `type:'web'` app has no auth middleware, so `auth.strategies` in a web `app.config.ts` does nothing. Configure your IdP (`supabaseStrategy`, `workosStrategy`, the built-in password strategy, …) on the api's `app.config.ts`.
954
+ - **`ctx.query` forwards the request's cookie automatically.** A server-side loader's `ctx.query` sends the browser's `Cookie` header on the one-shot `POST /rpc`, so the api resolves the SAME Subject + tenant it would over the WebSocket. You never thread a token through by hand — a logged-in user's cookie-mode session (e.g. `@supabase/ssr`'s `sb-<ref>-auth-token`) is verified by the api's strategies, and tenant-scoped queries return that user's rows during SSR.
955
+
956
+ So a cookie-mode Supabase app configures `supabaseStrategy({ cookieName: 'sb-<ref>-auth-token' })` on the **api**; the web loader's `ctx.query` then authenticates for free. See [Supabase Auth](/docs/plugins/auth-supabase).
957
+
800
958
  ## Errors from loaders
801
959
 
802
960
  If the loader throws, the framework:
@@ -383,6 +383,30 @@ expect(rows.map((r) => r.id)).not.toContain('bobs-ticket') // the rule, asserte
383
383
 
384
384
  Pass `rowFilter:` — as above — to scope a filter to **this context only**. `setRowFilter` is process-global: registered in one test it silently constrains every later test in the same worker, and a forgotten `afterEach` surfaces as a failure in an unrelated file. Either way the resolution is the runtime's own, so the retry schedule, the system bypass and the `onLoadError` policy behave identically: a filter whose `load` fails refuses the read (with `RowFilterUnavailable`, or zero rows under `onLoadError: 'deny'`) rather than quietly returning everything.
385
385
 
386
+ ### Filters over SHARED resources
387
+
388
+ A filter over rows the user *owns* needs no database — the subject carries the id. A filter over rows *shared with* the user must read a membership table, and `load` is `Effect<Ctx, unknown>` with `R = never`, so it cannot `yield*` a store service. Its only route is [`runAsSystem`](/docs/multi-tenancy/edge-cases), and `makeTestContext` wires its seeded store into that, so this resolves under test exactly as it does at runtime:
389
+
390
+ ```ts
391
+ const sharedLists: RowFilter<{ listIds: ReadonlyArray<string> }> = {
392
+ load: (subject) =>
393
+ Effect.promise(() =>
394
+ runAsSystem(async (sys) => {
395
+ const rows = await sys.store.select('listMembers').where('userId', String(subject.id)).all()
396
+ return { listIds: rows.map((r) => String(r.listId)) }
397
+ }),
398
+ ),
399
+ predicate: (ctx, table) => (table === 'lists' ? inSet('id', ctx.listIds) : undefined),
400
+ }
401
+
402
+ const bob = makeTestContext({ subject: bobSubject, store: mockStore(seed), rowFilter: sharedLists })
403
+ expect((await bob.store.select('lists').all()).map((r) => r.id)).not.toContain('alices-list')
404
+ ```
405
+
406
+ `makeTestContext` registers the **raw** data store + schema registry, so nothing is double-wrapped (`runAsSystem` applies the system-subject mixin wrap itself), and it is the outer store rather than a transactional view — matching production, where a `runAsSystem` block inside a mutation reads outside that mutation's transaction.
407
+
408
+ The handle is a process global, and the harness handles that in two layers. Registration is last-wins, so a bare `await runAsSystem(...)` written directly in a test also resolves. On top of that, each context re-points the handle at **its own** store for the span of its `load` and restores the previous value — necessary because two `makeTestContext` calls allocate two separate in-memory stores even from one seed object, so filter resolution must not depend on build order. Nothing leaks across test files (vitest gives each file a fresh module registry). Only the harness registers: an app that registers no handle still gets `runAsSystem: no data store available`, unchanged. If a test in the same file needs that refusal back, call `clearSystemStoreHandle()` from `@voltro/runtime`.
409
+
386
410
  ## The deterministic mocks
387
411
 
388
412
  ### `ctx.clock` — `MockClock`
@@ -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.7.0",
15
- "@voltro/cli": "0.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/protocol": "0.7.0",
19
- "@voltro/runtime": "0.7.0",
14
+ "@voltro/ai": "0.9.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/protocol": "0.9.0",
19
+ "@voltro/runtime": "0.9.0",
20
20
  "effect": "^3.21.2"
21
21
  },
22
22
  "devDependencies": {
23
- "@voltro/testing": "0.7.0",
23
+ "@voltro/testing": "0.9.0",
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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-auth": "0.7.0",
19
- "@voltro/protocol": "0.7.0",
20
- "@voltro/runtime": "0.7.0",
21
- "@voltro/sql-postgres": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-auth": "0.9.0",
19
+ "@voltro/protocol": "0.9.0",
20
+ "@voltro/runtime": "0.9.0",
21
+ "@voltro/sql-postgres": "0.9.0",
22
22
  "effect": "^3.21.2"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/testing": "0.7.0",
25
+ "@voltro/testing": "0.9.0",
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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-multitenancy": "0.7.0",
19
- "@voltro/protocol": "0.7.0",
20
- "@voltro/runtime": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-multitenancy": "0.9.0",
19
+ "@voltro/protocol": "0.9.0",
20
+ "@voltro/runtime": "0.9.0",
21
21
  "effect": "^3.21.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/testing": "0.7.0",
24
+ "@voltro/testing": "0.9.0",
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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-deactivation": "0.7.0",
19
- "@voltro/protocol": "0.7.0",
20
- "@voltro/runtime": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-deactivation": "0.9.0",
19
+ "@voltro/protocol": "0.9.0",
20
+ "@voltro/runtime": "0.9.0",
21
21
  "effect": "^3.21.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/testing": "0.7.0",
24
+ "@voltro/testing": "0.9.0",
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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-mail": "0.7.0",
19
- "@voltro/plugin-multitenancy": "0.7.0",
20
- "@voltro/protocol": "0.7.0",
21
- "@voltro/runtime": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-mail": "0.9.0",
19
+ "@voltro/plugin-multitenancy": "0.9.0",
20
+ "@voltro/protocol": "0.9.0",
21
+ "@voltro/runtime": "0.9.0",
22
22
  "effect": "^3.21.2",
23
23
  "react": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/testing": "0.7.0",
26
+ "@voltro/testing": "0.9.0",
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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-multitenancy": "0.7.0",
19
- "@voltro/plugin-storage": "0.7.0",
20
- "@voltro/protocol": "0.7.0",
21
- "@voltro/runtime": "0.7.0",
22
- "@voltro/sql-mysql": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-multitenancy": "0.9.0",
19
+ "@voltro/plugin-storage": "0.9.0",
20
+ "@voltro/protocol": "0.9.0",
21
+ "@voltro/runtime": "0.9.0",
22
+ "@voltro/sql-mysql": "0.9.0",
23
23
  "effect": "^3.21.2"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/testing": "0.7.0",
26
+ "@voltro/testing": "0.9.0",
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.7.0",
14
- "@voltro/database": "0.7.0",
15
- "@voltro/env": "0.7.0",
16
- "@voltro/plugin-multitenancy": "0.7.0",
17
- "@voltro/plugin-storage": "0.7.0",
18
- "@voltro/protocol": "0.7.0",
19
- "@voltro/runtime": "0.7.0",
13
+ "@voltro/cli": "0.9.0",
14
+ "@voltro/database": "0.9.0",
15
+ "@voltro/env": "0.9.0",
16
+ "@voltro/plugin-multitenancy": "0.9.0",
17
+ "@voltro/plugin-storage": "0.9.0",
18
+ "@voltro/protocol": "0.9.0",
19
+ "@voltro/runtime": "0.9.0",
20
20
  "effect": "^3.21.2"
21
21
  },
22
22
  "devDependencies": {
23
- "@voltro/testing": "0.7.0",
23
+ "@voltro/testing": "0.9.0",
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.7.0",
15
- "@voltro/database": "0.7.0",
16
- "@voltro/env": "0.7.0",
17
- "@voltro/plugin-governance": "0.7.0",
18
- "@voltro/plugin-multitenancy": "0.7.0",
19
- "@voltro/protocol": "0.7.0",
20
- "@voltro/runtime": "0.7.0",
14
+ "@voltro/cli": "0.9.0",
15
+ "@voltro/database": "0.9.0",
16
+ "@voltro/env": "0.9.0",
17
+ "@voltro/plugin-governance": "0.9.0",
18
+ "@voltro/plugin-multitenancy": "0.9.0",
19
+ "@voltro/protocol": "0.9.0",
20
+ "@voltro/runtime": "0.9.0",
21
21
  "effect": "^3.21.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/testing": "0.7.0",
24
+ "@voltro/testing": "0.9.0",
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.7.0",
15
- "@voltro/database": "0.7.0",
16
- "@voltro/env": "0.7.0",
17
- "@voltro/plugin-multitenancy": "0.7.0",
18
- "@voltro/protocol": "0.7.0",
19
- "@voltro/runtime": "0.7.0",
20
- "@voltro/workflow": "0.7.0",
14
+ "@voltro/cli": "0.9.0",
15
+ "@voltro/database": "0.9.0",
16
+ "@voltro/env": "0.9.0",
17
+ "@voltro/plugin-multitenancy": "0.9.0",
18
+ "@voltro/protocol": "0.9.0",
19
+ "@voltro/runtime": "0.9.0",
20
+ "@voltro/workflow": "0.9.0",
21
21
  "effect": "^3.21.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/testing": "0.7.0",
24
+ "@voltro/testing": "0.9.0",
25
25
  "typescript": "^5.7.0",
26
26
  "vitest": "^3.0.0"
27
27
  }
@@ -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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-flags": "0.7.0",
19
- "@voltro/plugin-multitenancy": "0.7.0",
20
- "@voltro/protocol": "0.7.0",
21
- "@voltro/runtime": "0.7.0",
22
- "@voltro/sql-postgres": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-flags": "0.9.0",
19
+ "@voltro/plugin-multitenancy": "0.9.0",
20
+ "@voltro/protocol": "0.9.0",
21
+ "@voltro/runtime": "0.9.0",
22
+ "@voltro/sql-postgres": "0.9.0",
23
23
  "effect": "^3.21.2"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/testing": "0.7.0",
26
+ "@voltro/testing": "0.9.0",
27
27
  "typescript": "^5.7.0",
28
28
  "vitest": "^3.0.0"
29
29
  }
@@ -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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-audit": "0.7.0",
19
- "@voltro/plugin-governance": "0.7.0",
20
- "@voltro/protocol": "0.7.0",
21
- "@voltro/runtime": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-audit": "0.9.0",
19
+ "@voltro/plugin-governance": "0.9.0",
20
+ "@voltro/protocol": "0.9.0",
21
+ "@voltro/runtime": "0.9.0",
22
22
  "effect": "^3.21.2"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/testing": "0.7.0",
25
+ "@voltro/testing": "0.9.0",
26
26
  "typescript": "^5.7.0",
27
27
  "vitest": "^3.0.0"
28
28
  }
@@ -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.7.0",
15
- "@voltro/database": "0.7.0",
16
- "@voltro/env": "0.7.0",
17
- "@voltro/kv": "0.7.0",
18
- "@voltro/plugin-multitenancy": "0.7.0",
19
- "@voltro/protocol": "0.7.0",
20
- "@voltro/runtime": "0.7.0",
14
+ "@voltro/cli": "0.9.0",
15
+ "@voltro/database": "0.9.0",
16
+ "@voltro/env": "0.9.0",
17
+ "@voltro/kv": "0.9.0",
18
+ "@voltro/plugin-multitenancy": "0.9.0",
19
+ "@voltro/protocol": "0.9.0",
20
+ "@voltro/runtime": "0.9.0",
21
21
  "effect": "^3.21.2"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/testing": "0.7.0",
24
+ "@voltro/testing": "0.9.0",
25
25
  "typescript": "^5.7.0",
26
26
  "vitest": "^3.0.0"
27
27
  }
@@ -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.7.0",
16
- "@voltro/database": "0.7.0",
17
- "@voltro/env": "0.7.0",
18
- "@voltro/plugin-moderation": "0.7.0",
19
- "@voltro/plugin-multitenancy": "0.7.0",
20
- "@voltro/protocol": "0.7.0",
21
- "@voltro/runtime": "0.7.0",
15
+ "@voltro/cli": "0.9.0",
16
+ "@voltro/database": "0.9.0",
17
+ "@voltro/env": "0.9.0",
18
+ "@voltro/plugin-moderation": "0.9.0",
19
+ "@voltro/plugin-multitenancy": "0.9.0",
20
+ "@voltro/protocol": "0.9.0",
21
+ "@voltro/runtime": "0.9.0",
22
22
  "effect": "^3.21.2"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/testing": "0.7.0",
25
+ "@voltro/testing": "0.9.0",
26
26
  "typescript": "^5.7.0",
27
27
  "vitest": "^3.0.0"
28
28
  }