@voltro/cli 0.59.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/dist/apiBuild-5U7PxjLa.js +2 -0
- package/dist/{apiBuild-BU5cd-Fn.js → apiBuild-C5HhMAEm.js} +2 -2
- package/dist/bin.js +1 -1
- package/dist/{build-uRMZrT8z.js → build-DQr7SsKK.js} +21 -17
- package/dist/{checkCommand-C1CQc8lD.js → checkCommand-ByaOhdc8.js} +1 -1
- package/dist/{checkCommand-DC7k1_hP.js → checkCommand-CvSheSdb.js} +3 -3
- package/dist/{codegenCommand-kS3Twuc9.js → codegenCommand-CI0mpjxo.js} +1 -1
- package/dist/{codemodRunner-kNnh1I7_.js → codemodRunner-Drb-H-jr.js} +258 -159
- package/dist/{commands-cu34ENUT.js → commands-pCItjdHG.js} +21 -21
- package/dist/{dashboardCommand-gG1y4PFG.js → dashboardCommand-O-ToEgiH.js} +3 -3
- package/dist/{dataCommand-hNOGKz4K.js → dataCommand-mn1Zc88W.js} +1 -1
- package/dist/{dev-Ce2gEziv.js → dev-dSH-Pkb2.js} +1 -1
- package/dist/{dev-Dff_pdik.js → dev-kn9-7RXZ.js} +1 -1
- package/dist/doctorCommand-BVat2ejT.js +2 -0
- package/dist/{doctorCommand-CGjefPmk.js → doctorCommand-ClB7Ucz5.js} +13 -13
- package/dist/{dormancyCommand-DYgI0Kf-.js → dormancyCommand-DZupfuq-.js} +1 -1
- package/dist/{embeddingsCommand-yJivQSrI.js → embeddingsCommand-D8_GquVX.js} +1 -1
- package/dist/{envCommand-ORJUf8r_.js → envCommand-DRjVSXRp.js} +2 -2
- package/dist/{evolveCommand-8CuFBlbQ.js → evolveCommand-Cb0C2b3V.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-D02A4WI2.js → infoCommand-BlKnoY2D.js} +1 -1
- package/dist/{probeCommand-BUYzc0CT.js → probeCommand-DYsOFm62.js} +1 -1
- package/dist/{runtimeTrace-DDuKJgVi.js → runtimeTrace-BI3kdicc.js} +1 -1
- package/dist/{sdkgen-vq1fF9D_.js → sdkgen-DZ4zCtV4.js} +1 -1
- package/dist/serveCommand-CwK_6Wxt.js +2 -0
- package/dist/{serveCommand-Dr4KXmTS.js → serveCommand-DnxqCQ16.js} +3 -3
- package/dist/serveEntry.js +1 -1
- package/dist/{start-C75w6ftA.js → start-DE4ixCTi.js} +1 -1
- package/dist/{start-DNtIlBcn.js → start-bqT4GRH0.js} +286 -226
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-Bw3mCUDT.js → updateCommand-B18ZQNFg.js} +1 -1
- package/dist/updateCommand-B4EgDD-m.js +2 -0
- package/dist/{webDev-CBN9KkyQ.js → webDev-CYjhmOTZ.js} +1 -1
- package/dist/{webDev-Bzb8ZFus.js → webDev-CuG3Ged-.js} +1 -1
- package/dist/{webhooksCommand-BPJQOGTe.js → webhooksCommand-Bs-HZ4wY.js} +1 -1
- package/package.json +37 -19
- package/templates/AGENTS.md +2 -2
- package/templates/agent-docs/_index.md +2 -2
- package/templates/agent-docs/_manifest.json +1 -1
- package/templates/agent-docs/data.md +10 -2
- package/templates/agent-docs/observability.md +7 -1
- package/templates/agent-docs/plugins/sentry.md +121 -17
- package/templates/agent-docs/schema-driven-ui.md +38 -10
- package/templates/agent-docs/whats-new.md +11 -41
- package/templates/apps/api-ai/package.json +6 -6
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +9 -9
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-row-history/package.json +8 -8
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/package.json +10 -10
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +7 -7
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +7 -7
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-auth/package.json +7 -7
- package/templates/apps/frontend-blank/package.json +6 -6
- package/templates/apps/frontend-cms/package.json +8 -8
- package/templates/apps/frontend-collab/package.json +9 -9
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +6 -6
- package/templates/apps/frontend-docs/package.json +8 -8
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/package.json +7 -7
- package/templates/apps/frontend-saas/package.json +7 -7
- package/templates/apps/frontend-spa/package.json +6 -6
- package/templates/apps/frontend-ssr/package.json +6 -6
- package/templates/apps/frontend-ssr-api/package.json +7 -7
- package/templates/apps/frontend-static-blog/package.json +8 -8
- package/templates/apps/frontend-status/package.json +7 -7
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-B9OvhITV.js +0 -2
- package/dist/doctorCommand-DXnNpKQT.js +0 -2
- package/dist/serveCommand-1R52mo6F.js +0 -2
- package/dist/updateCommand-inInBIAh.js +0 -2
package/dist/startEntry.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./codemodRunner-
|
|
1
|
+
import { n as e, r as t, t as n } from "./codemodRunner-Drb-H-jr.js";
|
|
2
2
|
import { basename as r, dirname as i, join as a, relative as o, resolve as s } from "node:path";
|
|
3
3
|
import { existsSync as c, readFileSync as l, readdirSync as u, statSync as d, writeFileSync as f } from "node:fs";
|
|
4
4
|
import { totalmem as p } from "node:os";
|
|
@@ -2556,7 +2556,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
|
|
|
2556
2556
|
return [
|
|
2557
2557
|
"",
|
|
2558
2558
|
"// Browser-side Sentry (frontend half of the deep integration). Errors +",
|
|
2559
|
-
"//
|
|
2559
|
+
"// page-owned rpc spans linked to their server traces flow to Sentry.",
|
|
2560
2560
|
n === void 0 ? `await initSentryBrowser(${JSON.stringify(r)})` : `await initSentryBrowser({ ...${JSON.stringify(r)}, ...(await __sentryOptions()) })`
|
|
2561
2561
|
];
|
|
2562
2562
|
}, Fo = (e) => e.env ? ["import './env.public'"] : [], Io = (e) => {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as e, T as t, m as n } from "./webDev-
|
|
1
|
+
import { C as e, T as t, m as n } from "./webDev-CYjhmOTZ.js";
|
|
2
2
|
export { n as loadConfig, e as tryRunWebServe, t as walkPagesTree };
|
|
@@ -222,7 +222,7 @@ createVerifier({ secret: [process.env.WEBHOOK_SECRET, process.env.WEBHOOK_SECRET
|
|
|
222
222
|
...t === void 0 ? {} : { payload: t }
|
|
223
223
|
};
|
|
224
224
|
}, S = u({ scope: "voltro:webhooks" }), C = ["--out", "--name"], w = async (e) => {
|
|
225
|
-
let { walk: t, loadDiscovered: n } = await import("./dev-
|
|
225
|
+
let { walk: t, loadDiscovered: n } = await import("./dev-kn9-7RXZ.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-il9ti-HE.js");
|
|
226
226
|
return r((await n(await t(e), { root: e })).events.map((e) => ({
|
|
227
227
|
file: e.file,
|
|
228
228
|
descriptor: e.descriptor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.0",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -892,6 +892,24 @@
|
|
|
892
892
|
"title": "UiStrings gained a required `connectAccount` section",
|
|
893
893
|
"kind": "manual"
|
|
894
894
|
},
|
|
895
|
+
{
|
|
896
|
+
"version": "0.60.0",
|
|
897
|
+
"id": "0.60.0/01_declared_failures_are_not_incidents",
|
|
898
|
+
"title": "Sentry no longer reports declared failures by default",
|
|
899
|
+
"kind": "manual"
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
"version": "0.61.0",
|
|
903
|
+
"id": "0.61.0/01_rpc_errors_have_kinds",
|
|
904
|
+
"title": "`RpcError` now carries an explicit failure kind",
|
|
905
|
+
"kind": "manual"
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
"version": "0.61.0",
|
|
909
|
+
"id": "0.61.0/02_sentry_rpc_spans_join_page",
|
|
910
|
+
"title": "Sentry rpc spans now join the active browser transaction",
|
|
911
|
+
"kind": "manual"
|
|
912
|
+
},
|
|
895
913
|
{
|
|
896
914
|
"version": "0.7.0",
|
|
897
915
|
"id": "0.7.0/01_row-filter-load-failure",
|
|
@@ -938,24 +956,24 @@
|
|
|
938
956
|
"@effect/platform-node": "^0.108.0",
|
|
939
957
|
"@effect/sql": "^0.52.0",
|
|
940
958
|
"@effect/workflow": "^0.19.0",
|
|
941
|
-
"@voltro/ai": "0.
|
|
942
|
-
"@voltro/cache": "0.
|
|
943
|
-
"@voltro/client": "0.
|
|
944
|
-
"@voltro/content": "0.
|
|
945
|
-
"@voltro/data-transfer": "0.
|
|
946
|
-
"@voltro/database": "0.
|
|
947
|
-
"@voltro/env": "0.
|
|
948
|
-
"@voltro/kv": "0.
|
|
949
|
-
"@voltro/logger": "0.
|
|
950
|
-
"@voltro/plugin-auth": "0.
|
|
951
|
-
"@voltro/plugin-broadcast": "0.
|
|
952
|
-
"@voltro/plugin-mail": "0.
|
|
953
|
-
"@voltro/plugin-storage": "0.
|
|
954
|
-
"@voltro/plugin-webhooks": "0.
|
|
955
|
-
"@voltro/protocol": "0.
|
|
956
|
-
"@voltro/runtime": "0.
|
|
957
|
-
"@voltro/serverless": "0.
|
|
958
|
-
"@voltro/workflow": "0.
|
|
959
|
+
"@voltro/ai": "0.61.0",
|
|
960
|
+
"@voltro/cache": "0.61.0",
|
|
961
|
+
"@voltro/client": "0.61.0",
|
|
962
|
+
"@voltro/content": "0.61.0",
|
|
963
|
+
"@voltro/data-transfer": "0.61.0",
|
|
964
|
+
"@voltro/database": "0.61.0",
|
|
965
|
+
"@voltro/env": "0.61.0",
|
|
966
|
+
"@voltro/kv": "0.61.0",
|
|
967
|
+
"@voltro/logger": "0.61.0",
|
|
968
|
+
"@voltro/plugin-auth": "0.61.0",
|
|
969
|
+
"@voltro/plugin-broadcast": "0.61.0",
|
|
970
|
+
"@voltro/plugin-mail": "0.61.0",
|
|
971
|
+
"@voltro/plugin-storage": "0.61.0",
|
|
972
|
+
"@voltro/plugin-webhooks": "0.61.0",
|
|
973
|
+
"@voltro/protocol": "0.61.0",
|
|
974
|
+
"@voltro/runtime": "0.61.0",
|
|
975
|
+
"@voltro/serverless": "0.61.0",
|
|
976
|
+
"@voltro/workflow": "0.61.0",
|
|
959
977
|
"chokidar": "^5.0.0",
|
|
960
978
|
"ioredis": "^5.11.1",
|
|
961
979
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.md
CHANGED
|
@@ -749,7 +749,7 @@ each plugin's own README.
|
|
|
749
749
|
|
|
750
750
|
| Topic | Open | Summary |
|
|
751
751
|
|---|---|---|
|
|
752
|
-
| **What's new in 0.
|
|
752
|
+
| **What's new in 0.61.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
753
753
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
754
754
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
755
755
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -832,7 +832,7 @@ each plugin's own README.
|
|
|
832
832
|
| row-history | `node_modules/@voltro/cli/templates/agent-docs/plugins/row-history.md` (or `node_modules/@voltro/plugin-row-history/README.md`) | Full row history + time-travel. audit() records who/when; row-history records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
833
833
|
| scim | `node_modules/@voltro/cli/templates/agent-docs/plugins/scim.md` (or `node_modules/@voltro/plugin-scim/README.md`) | SCIM 2.0 provisioning — Users + Groups REST endpoints so an enterprise IdP (Okta, Entra, OneLogin) can create / update / deactivate users in your app. |
|
|
834
834
|
| search | `node_modules/@voltro/cli/templates/agent-docs/plugins/search.md` (or `node_modules/@voltro/plugin-search/README.md`) | Keep an external search index (Typesense / Meilisearch / Algolia) in sync with your tables via the ChangeEvent tap, query it tenant-scoped through a typed action + hook. |
|
|
835
|
-
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and
|
|
835
|
+
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and controlled performance traces. |
|
|
836
836
|
| soft-delete | `node_modules/@voltro/cli/templates/agent-docs/plugins/soft-delete.md` (or `node_modules/@voltro/plugin-soft-delete/README.md`) | The softDelete() schema mixin — deletedAt / deletedBy columns, delete() redirected to an UPDATE, default reads filtered, hardDelete() bypass. |
|
|
837
837
|
| sso-saml | `node_modules/@voltro/cli/templates/agent-docs/plugins/sso-saml.md` (or `node_modules/@voltro/plugin-sso-saml/README.md`) | Enterprise SAML 2.0 SSO — SP-initiated login, ACS assertion consumer, SP metadata. Signature verification via @node-saml/node-saml; framework session minting built in. |
|
|
838
838
|
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / MinIO (R2 and GCS via their S3 interop) / Azure / database / filesystem / memory providers, presigned URLs, a dashboard browser. |
|
|
@@ -9,7 +9,7 @@ each plugin's own README.
|
|
|
9
9
|
|
|
10
10
|
| Topic | Open | Summary |
|
|
11
11
|
|---|---|---|
|
|
12
|
-
| **What's new in 0.
|
|
12
|
+
| **What's new in 0.61.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
|
|
13
13
|
| AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
|
|
14
14
|
| Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
|
|
15
15
|
| Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
|
|
@@ -92,7 +92,7 @@ each plugin's own README.
|
|
|
92
92
|
| row-history | `node_modules/@voltro/cli/templates/agent-docs/plugins/row-history.md` (or `node_modules/@voltro/plugin-row-history/README.md`) | Full row history + time-travel. audit() records who/when; row-history records what-changed-to-what — a value snapshot of every row on every write, with as-of queries. |
|
|
93
93
|
| scim | `node_modules/@voltro/cli/templates/agent-docs/plugins/scim.md` (or `node_modules/@voltro/plugin-scim/README.md`) | SCIM 2.0 provisioning — Users + Groups REST endpoints so an enterprise IdP (Okta, Entra, OneLogin) can create / update / deactivate users in your app. |
|
|
94
94
|
| search | `node_modules/@voltro/cli/templates/agent-docs/plugins/search.md` (or `node_modules/@voltro/plugin-search/README.md`) | Keep an external search index (Typesense / Meilisearch / Algolia) in sync with your tables via the ChangeEvent tap, query it tenant-scoped through a typed action + hook. |
|
|
95
|
-
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and
|
|
95
|
+
| sentry | `node_modules/@voltro/cli/templates/agent-docs/plugins/sentry.md` (or `node_modules/@voltro/plugin-sentry/README.md`) | Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and controlled performance traces. |
|
|
96
96
|
| soft-delete | `node_modules/@voltro/cli/templates/agent-docs/plugins/soft-delete.md` (or `node_modules/@voltro/plugin-soft-delete/README.md`) | The softDelete() schema mixin — deletedAt / deletedBy columns, delete() redirected to an UPDATE, default reads filtered, hardDelete() bypass. |
|
|
97
97
|
| sso-saml | `node_modules/@voltro/cli/templates/agent-docs/plugins/sso-saml.md` (or `node_modules/@voltro/plugin-sso-saml/README.md`) | Enterprise SAML 2.0 SSO — SP-initiated login, ACS assertion consumer, SP metadata. Signature verification via @node-saml/node-saml; framework session minting built in. |
|
|
98
98
|
| storage | `node_modules/@voltro/cli/templates/agent-docs/plugins/storage.md` (or `node_modules/@voltro/plugin-storage/README.md`) | File storage behind one StorageService — public objects served direct from the bucket/CDN, private objects gated by an access policy + per-object grants. S3 / MinIO (R2 and GCS via their S3 interop) / Azure / database / filesystem / memory providers, presigned URLs, a dashboard browser. |
|
|
@@ -660,7 +660,7 @@
|
|
|
660
660
|
{
|
|
661
661
|
"slug": "sentry",
|
|
662
662
|
"title": "Sentry",
|
|
663
|
-
"description": "Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and
|
|
663
|
+
"description": "Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and controlled performance traces.",
|
|
664
664
|
"pkg": "@voltro/plugin-sentry",
|
|
665
665
|
"doc": "plugins/sentry.md",
|
|
666
666
|
"module": "agent-docs/plugins/sentry.md"
|
|
@@ -1220,13 +1220,21 @@ try {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
```
|
|
1222
1222
|
|
|
1223
|
-
**Trace id for debugging.** An error caught from `useMutation` / `useAction`
|
|
1223
|
+
**Trace id for debugging.** An error caught from `useMutation` / `useAction`
|
|
1224
|
+
carries the **non-enumerable client-span id `__voltroTraceId`**:
|
|
1224
1225
|
|
|
1225
1226
|
```ts
|
|
1226
1227
|
const traceId = (err as { __voltroTraceId?: string }).__voltroTraceId
|
|
1227
|
-
//
|
|
1228
|
+
// If the server accepted the call: `voltro logs --trace <traceId>`
|
|
1228
1229
|
```
|
|
1229
1230
|
|
|
1231
|
+
For a declared handler error, the server necessarily accepted the call and the
|
|
1232
|
+
id joins to its span. For a transport error it may not: the connection can fail
|
|
1233
|
+
before the request arrived or after the handler ran but before its response
|
|
1234
|
+
returned. Use `kind` from
|
|
1235
|
+
[`useOnRpcError`](/docs/ui/client-utilities/use-on-rpc-error) for that distinction;
|
|
1236
|
+
the presence of the id alone proves only that the client span existed.
|
|
1237
|
+
|
|
1230
1238
|
`errorTag` lives in `@voltro/protocol` rather than in the client for a reason
|
|
1231
1239
|
worth knowing before you decide where your own error handling goes: **`_tag` is a
|
|
1232
1240
|
wire concept, and protocol owns the wire.** So a shared error handler in a package
|
|
@@ -213,6 +213,12 @@ Trace continuity is **automatic and on by default** in `voltro dev` — a buffer
|
|
|
213
213
|
- **api → api is automatic over HTTP.** An action or handler that calls another api via the framework `HttpClient` sends a W3C `traceparent` header; an inbound `*.webhook.tsx` route (an incoming HTTP handler) **continues** the caller's trace. So frontend → api Y → api Z (via webhook / HTTP) is **one** trace.
|
|
214
214
|
- **Every log line carries it.** Handlers stamp `fields.traceId` onto each log line they emit, so [`voltro logs --trace <id>`](/docs/observability/cli) returns the whole chain across hops, in order.
|
|
215
215
|
|
|
216
|
+
The client span exists before the transport proves delivery. If a connection
|
|
217
|
+
fails, its `traceId` can therefore have no matching server span — or the server
|
|
218
|
+
may have completed the handler and lost only the response. In rpc-error policy,
|
|
219
|
+
use [`RpcError.kind`](/docs/ui/client-utilities/use-on-rpc-error), never the mere
|
|
220
|
+
presence of `traceId`, to distinguish transport failures from handler failures.
|
|
221
|
+
|
|
216
222
|
```
|
|
217
223
|
client.mutation.placeOrder traceId=4bf92f35…
|
|
218
224
|
└─ mutation.placeOrder traceId=4bf92f35… (api Y, ctx.request.traceId)
|
|
@@ -259,7 +265,7 @@ This is the fastest way to answer "where did this error come from?" — far fast
|
|
|
259
265
|
|
|
260
266
|
The same trace continuity flows to a vendor APM when you install a deep-observability plugin — no `OTEL_*` env. Each contributes an OTel span-processor to the framework's tracer via `contributeObservability`, so the vendor runs as a **consumer** of the existing tracer (the in-app Traces panel keeps working) and receives the identical spans:
|
|
261
267
|
|
|
262
|
-
- [`@voltro/plugin-sentry`](/docs/plugins/sentry) — every mutation/query/action error becomes a Sentry issue correlated to its `trace_id` + `span_id`, with the request's log lines as breadcrumbs;
|
|
268
|
+
- [`@voltro/plugin-sentry`](/docs/plugins/sentry) — every mutation/query/action error becomes a Sentry issue correlated to its `trace_id` + `span_id`, with the request's log lines as breadcrumbs; server performance traces are on by default (`traces: false` disables them). The browser integration keeps rpc markers inside the sampled page/navigation transaction and links each marker to its Effect/server trace.
|
|
263
269
|
- [`@voltro/plugin-datadog`](/docs/plugins/datadog) — framework spans → the Datadog Agent's OTLP receiver (`traces: true`), plus log forwarding with `dd.trace_id` correlation.
|
|
264
270
|
|
|
265
271
|
See [Observability › Routing traces to a vendor](/docs/observability/overview#routing-traces-to-a-vendor) for the contribution surface + the sampler caveat.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sentry
|
|
2
2
|
|
|
3
|
-
> Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and
|
|
3
|
+
> Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and controlled performance traces.
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
<!-- source: en/plugins/sentry.md -->
|
|
10
10
|
## Sentry
|
|
11
11
|
|
|
12
|
-
_Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and
|
|
12
|
+
_Deep Sentry integration — errors correlated to the distributed trace, breadcrumbs from the log sink, and controlled performance traces._
|
|
13
13
|
|
|
14
|
-
`@voltro/plugin-sentry` is the deep Sentry integration: every mutation/query/action failure becomes a Sentry issue **correlated to the active distributed trace** (trace_id + span_id) and carrying the request's recent log lines as breadcrumbs.
|
|
14
|
+
`@voltro/plugin-sentry` is the deep Sentry integration: every mutation/query/action failure becomes a Sentry issue **correlated to the active distributed trace** (trace_id + span_id) and carrying the request's recent log lines as breadcrumbs. The framework's spans flow to Sentry as transactions by default.
|
|
15
15
|
|
|
16
16
|
Uses the official `@sentry/node` SDK in **OpenTelemetry-consumer mode** — it consumes the framework's existing tracer (it never becomes the global provider), so the in-app [Traces dashboard](/docs/observability/distributed-tracing) stays intact.
|
|
17
17
|
|
|
@@ -29,7 +29,7 @@ export default {
|
|
|
29
29
|
// dsn defaults to SENTRY_DSN; without a valid DSN the plugin is a no-op.
|
|
30
30
|
environment: 'production', // or SENTRY_ENVIRONMENT
|
|
31
31
|
release: '1.4.2', // or SENTRY_RELEASE
|
|
32
|
-
traces: true, // route the framework's spans → Sentry (default
|
|
32
|
+
traces: true, // route the framework's spans → Sentry (default on)
|
|
33
33
|
tracesSampleRate: 1.0,
|
|
34
34
|
profiling: true, // continuous CPU profiler via @sentry/profiling-node
|
|
35
35
|
}),
|
|
@@ -46,7 +46,7 @@ export default {
|
|
|
46
46
|
| `dsn` | `string` | `SENTRY_DSN` env | No valid DSN → the plugin is inert (no interceptors registered). |
|
|
47
47
|
| `environment` | `string` | `SENTRY_ENVIRONMENT` env | Sentry `environment` tag + `deployment.environment` resource attribute. |
|
|
48
48
|
| `release` | `string` | `SENTRY_RELEASE` env | Release-health + source-map matching; also `service.version`. |
|
|
49
|
-
| `traces` | `boolean` | `
|
|
49
|
+
| `traces` | `boolean` | `true` | Route the framework's OTel spans to Sentry as transactions. Off → errors + breadcrumbs only. |
|
|
50
50
|
| `tracesSampleRate` | `number` | `1.0` | Applied by the Sentry client (not a tracer sampler). Only relevant with `traces`. |
|
|
51
51
|
| `profiling` | `boolean` | `false` | Continuous CPU profiler via `@sentry/profiling-node` (lazy; warns if not installed). |
|
|
52
52
|
| `profilesSampleRate` | `number` | `1.0` | Only relevant with `profiling`. |
|
|
@@ -73,15 +73,15 @@ This guarantees the error links to its trace in Sentry **regardless** of the Eff
|
|
|
73
73
|
|
|
74
74
|
The framework log sink feeds a bounded **per-trace** ring (capped per-trace + LRU + TTL), keyed on each log line's `traceId`. On an error the trace's breadcrumbs are attached to the event; traces that never error are reaped by the TTL. So a Sentry issue shows the exact log lines leading up to it — for that request only.
|
|
75
75
|
|
|
76
|
-
##
|
|
76
|
+
## Server traces
|
|
77
77
|
|
|
78
78
|
The plugin contributes a `SentrySpanProcessor` to the framework's tracer (via the plugin observability surface — see [Observability › Routing traces to a vendor](/docs/observability/overview#routing-traces-to-a-vendor)). Every framework span becomes a Sentry transaction, sharing the same trace id the errors carry — so in Sentry you land on an error inside its full distributed trace.
|
|
79
79
|
|
|
80
80
|
We deliberately do **not** install a `SentrySampler` as the tracer's sampler: that would gate recording for the WHOLE tracer and blind the framework's own in-app Traces buffer. The framework keeps its always-on sampler; `tracesSampleRate` is applied by the Sentry client.
|
|
81
81
|
|
|
82
|
-
##
|
|
82
|
+
## Correlating the browser page with server traces
|
|
83
83
|
|
|
84
|
-
The plugin ships a browser half at `@voltro/plugin-sentry/web
|
|
84
|
+
The plugin ships a browser half at `@voltro/plugin-sentry/web`. It records one page-load or navigation transaction with rpc child spans, while each rpc child links to the Effect trace continued by the server. This avoids a transaction and network envelope per rpc call without losing the connection between browser and server work. Turn it on in the web app's `app.config.ts`:
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
87
|
// apps/web/app.config.ts
|
|
@@ -94,6 +94,8 @@ export default {
|
|
|
94
94
|
environment: 'production',
|
|
95
95
|
release: '1.4.2', // MUST match the api's `release` for unified release health
|
|
96
96
|
tracesSampleRate: 1.0,
|
|
97
|
+
browserTracing: true,
|
|
98
|
+
rpcSpans: true,
|
|
97
99
|
},
|
|
98
100
|
}
|
|
99
101
|
```
|
|
@@ -102,7 +104,22 @@ The framework's generated entry calls `initSentryBrowser()` before mount (no cod
|
|
|
102
104
|
|
|
103
105
|
What it does:
|
|
104
106
|
- **Inits `@sentry/react`** with `browserTracingIntegration` — page loads + client navigations become Sentry transactions, and unhandled errors / promise rejections are captured by the SDK's default global handlers.
|
|
105
|
-
- **
|
|
107
|
+
- **Keeps rpc spans page-owned.** Every framework rpc call (`useMutation`/`useSubscription`/`useAction`) already wraps itself in an Effect client span and propagates that span's W3C `traceId`+`spanId` to the api. The browser half adds a child to the currently active page/navigation transaction and attaches the Effect/server trace as a span link. If no browser transaction is active, it emits no rpc root transaction.
|
|
108
|
+
- **Honours sampling.** `tracesSampleRate` decides whether the page/navigation root is recorded; its rpc children inherit that decision. The bridge does not inject a sampled parent decision.
|
|
109
|
+
|
|
110
|
+
Browser trace controls:
|
|
111
|
+
|
|
112
|
+
| Option | Default | Effect |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `tracesSampleRate` | `1.0` | Samples page-load/navigation transactions and therefore their rpc children. Error reporting is independent. |
|
|
115
|
+
| `browserTracing` | `true` | Installs page-load/navigation instrumentation. `false` also defaults `rpcSpans` to off. |
|
|
116
|
+
| `rpcSpans` | same as `browserTracing` | Adds rpc children to an active browser transaction. `false` keeps browser errors and page traces but removes the rpc bridge. Set it to `true` with `browserTracing:false` only if your app creates active Sentry transactions itself. |
|
|
117
|
+
|
|
118
|
+
At browser boot the plugin prints the effective values, including the mode:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
[@voltro/plugin-sentry] browser active { tracesSampleRate: 1, browserTracing: true, rpcSpans: true, rpcSpanMode: 'page-child-with-server-link' }
|
|
122
|
+
```
|
|
106
123
|
|
|
107
124
|
> **Manual wiring** (no app.config field, e.g. an app that builds its own entry): call it yourself once at boot —
|
|
108
125
|
> ```ts
|
|
@@ -157,9 +174,9 @@ in deployment env. Setting one as a deployment variable deploys cleanly and does
|
|
|
157
174
|
nothing; `voltro start` warns when it finds one, naming whether the bundle
|
|
158
175
|
carries that key at all.
|
|
159
176
|
|
|
160
|
-
The two halves are independent: the api plugin alone gives you server traces + errors; add the web half for
|
|
177
|
+
The two halves are independent: the api plugin alone gives you server traces + errors; add the web half for page performance, browser errors, rpc markers, and links back to the server traces. Both tracing halves are on by default, so an app with the plugin on the api and `@voltro/plugin-sentry` as a dependency of the web app gets server transactions plus one sampled browser transaction per page/navigation from one `SENTRY_DSN` plus one `VOLTRO_PUBLIC_SENTRY_DSN`. Give both halves the same `release`.
|
|
161
178
|
|
|
162
|
-
**Traces default to on, at `tracesSampleRate: 1.0`.** That is
|
|
179
|
+
**Traces default to on, at `tracesSampleRate: 1.0`.** That is explicit, not a production recommendation. Lower the rate independently on api and web to fit the trace budget. Set api `traces:false` to keep server errors and breadcrumbs only; set web `browserTracing:false` to keep browser error capture without automatic performance transactions; or use web `rpcSpans:false` to keep page performance while omitting rpc child spans.
|
|
163
180
|
|
|
164
181
|
## Source maps (every bundle, browser and server)
|
|
165
182
|
|
|
@@ -243,11 +260,97 @@ Two details worth knowing before the first upload:
|
|
|
243
260
|
store, and it fails with `SSL peer certificate … unable to get local issuer
|
|
244
261
|
certificate`. Add `ca-certificates` to the build stage.
|
|
245
262
|
|
|
246
|
-
**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
263
|
+
**Debug ids are what make a server frame resolve, and they need their own
|
|
264
|
+
step.** `sentry-cli sourcemaps upload` does **not** write them — `inject` is a
|
|
265
|
+
separate subcommand, and `upload` only uses ids that are already there, falling
|
|
266
|
+
back to matching on the artifact NAME when they are not.
|
|
267
|
+
|
|
268
|
+
That fallback cannot work for a server bundle: the artifact is named from
|
|
269
|
+
`--url-prefix` (`~/chunk-ABC.js`) while the frame carries the absolute path the
|
|
270
|
+
node process loaded (`/workspace/…/serveBundle/chunk-ABC.js`), and nothing
|
|
271
|
+
rewrites either side. Measured downstream before this was fixed: 4300 artifacts
|
|
272
|
+
uploaded, the release finalised, every frame still minified — nothing red, which
|
|
273
|
+
is the failure this page's own warning describes.
|
|
274
|
+
|
|
275
|
+
`voltro build` runs `inject` first now, so the ids are in both the shipped JS and
|
|
276
|
+
the map before anything is uploaded. The URL prefix stays as the fallback for the
|
|
277
|
+
browser bundle, whose frames really are URLs under `~/assets`.
|
|
278
|
+
|
|
279
|
+
## The web server is a SECOND process — and it needs the DSN too
|
|
280
|
+
|
|
281
|
+
A framework app runs two server processes. `voltro serve` is the api, where
|
|
282
|
+
`sentryPlugin()` initialises the SDK through the plugin lifecycle. `voltro start`
|
|
283
|
+
is the **web server** — SSR, loaders, ISR, the revalidation legs — and it has no
|
|
284
|
+
plugin lifecycle at all.
|
|
285
|
+
|
|
286
|
+
It now initialises Sentry itself when `SENTRY_DSN` is set:
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
SENTRY_DSN=… SENTRY_ENVIRONMENT=production SENTRY_RELEASE=1.4.2 voltro start
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
{"level":"info","msg":"sentry active","environment":"production","release":"1.4.2","traces":false,"profiling":false,"process":"web"}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Same line and same field names as the api half, from the same function — one
|
|
297
|
+
vocabulary across both pods, plus `process` so a collector can tell them apart.
|
|
298
|
+
|
|
299
|
+
**Why this was a hole rather than a missing log line.** An SSR shell throw is
|
|
300
|
+
caught, logged, and answered with a 500. No browser ever renders it, so the
|
|
301
|
+
client-side ErrorBoundary bridge below cannot see it either: the error existed
|
|
302
|
+
only in the pod's stdout. Both ends of the integration worked and the middle was
|
|
303
|
+
dark.
|
|
304
|
+
|
|
305
|
+
Three things to know:
|
|
306
|
+
|
|
307
|
+
- **`@voltro/plugin-sentry` must be installed in the WEB app**, not only the api
|
|
308
|
+
— the web server resolves the shared init from it. With a DSN set and the
|
|
309
|
+
package missing, the boot says so and names the command; it does not fail.
|
|
310
|
+
- **`traces` is `false` here, deliberately.** The api half contributes a span
|
|
311
|
+
processor to the framework's tracer. The web server has no tracer, so
|
|
312
|
+
initialising with tracing on would put `traces: true` in a boot line while
|
|
313
|
+
nothing produces a span.
|
|
314
|
+
- **No DSN is silent.** On the api side `sentryPlugin()` in your config is a
|
|
315
|
+
declaration, so an inert one is worth a warning. Here there is no declaration
|
|
316
|
+
to contradict, and a line about an integration you did not ask for is noise.
|
|
317
|
+
|
|
318
|
+
**Re-run `voltro build` after upgrading.** Production `voltro start` loads the
|
|
319
|
+
app's precompiled start bundle, so the web half picks this up when that bundle
|
|
320
|
+
is rebuilt — not from the framework version alone.
|
|
321
|
+
|
|
322
|
+
## What reaches Sentry — a declared failure does not
|
|
323
|
+
|
|
324
|
+
A procedure's `error:` union is a **contract**. Somebody wrote the outcome down,
|
|
325
|
+
the client gets it typed and branches on it, and it describes a state of the
|
|
326
|
+
world: `AccessDeniedError` on a team you are not a member of is the system
|
|
327
|
+
working. A **defect** is the opposite — nobody foresaw it. Sentry is built for
|
|
328
|
+
the second.
|
|
329
|
+
|
|
330
|
+
So a cause that carries only declared failures is **not** reported:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
sentryPlugin() // captureFailures: false — the default
|
|
334
|
+
sentryPlugin({ captureFailures: true }) // report them again
|
|
335
|
+
sentryPlugin({ captureFailures: (e) => (e as { _tag?: string })._tag === 'PaymentDeclined' })
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
- **Defects are always reported**, including a defect that arrived beside a
|
|
339
|
+
failure — the rule is failures-*only*, so one cannot hide the other.
|
|
340
|
+
- **Both halves take the option.** A rejected call is an rpc error on the client
|
|
341
|
+
too, so `initSentryBrowser({ captureFailures })` applies the same rule to
|
|
342
|
+
`rpc.*` events that carry a `_tag`. Set them together, or the api goes quiet
|
|
343
|
+
while the browser keeps filing the same rejection.
|
|
344
|
+
- **Route render failures and `reportClientError` are never filtered.** Nobody
|
|
345
|
+
declared those, which is the whole distinction.
|
|
346
|
+
- **The boot line names it** — `sentry active … captureFailures=false` — because
|
|
347
|
+
a default the framework picks for you is one nobody finds again.
|
|
348
|
+
|
|
349
|
+
Why the default is off: an issue list that fills with the expected is one nobody
|
|
350
|
+
reads. Every invalid form value, every click on a resource without access and
|
|
351
|
+
every call on a deleted row was a `level: error` issue. If Sentry is where you
|
|
352
|
+
see errors at all, `captureFailures: true` is the one-line answer and it is a
|
|
353
|
+
reasonable choice.
|
|
251
354
|
|
|
252
355
|
## Catching errors — what's automatic, what's manual
|
|
253
356
|
|
|
@@ -257,7 +360,8 @@ Once the two halves are wired, most errors are captured **with no per-call code*
|
|
|
257
360
|
|---|---|---|
|
|
258
361
|
| rpc handler throw (mutation/query/action) | ✅ auto | server interceptor → Sentry, trace-correlated + breadcrumbs |
|
|
259
362
|
| Unhandled browser error / promise rejection | ✅ auto | `@sentry/react` global handlers (after `initSentryBrowser`) |
|
|
260
|
-
| **React render error** (any page, incl. catch-all `[...slug]`, `error.tsx` boundary) | ✅ auto | the framework's route ErrorBoundary publishes to a client-error bus → the Sentry web bridge captures it. React swallows boundary-caught errors before `window.onerror`, so this bridge is what makes them reach Sentry — tagged with the route + component stack. |
|
|
363
|
+
| **React render error, in the BROWSER** (any page, incl. catch-all `[...slug]`, `error.tsx` boundary) | ✅ auto | the framework's route ErrorBoundary publishes to a client-error bus → the Sentry web bridge captures it. React swallows boundary-caught errors before `window.onerror`, so this bridge is what makes them reach Sentry — tagged with the route + component stack. |
|
|
364
|
+
| **React render error, during SSR** — six sites: `ssr-shell`, `spa-layout-shell`, `render` (incl. a loader throw), `not-found-render`, `ppr-holes`, `swr-refresh` | ✅ auto, from the WEB server | a different process and a different path: the render never reaches a browser, so the row above cannot cover it. Captured by the web server's own Sentry (see the section above), tagged `voltro.process=web` and `voltro.stage`. The last three are `warn`-level — the request still serves something — and are reported anyway: quieter is not less real, and nothing else can see them. Needs `SENTRY_DSN` on the **web** deployment. |
|
|
261
365
|
| **Loader failure** (page/layout `loader` reject) | ✅ auto | same route bus path |
|
|
262
366
|
| Manual try/catch (event handler, async effect, anywhere) | ✋ one call | `reportClientError(error, context?)` |
|
|
263
367
|
| **Every other server primitive** — REST route (`defineRestRoute`), aggregate, subscriber, schedule, workflow, webhook, startup | ✅ auto | each primitive's error path publishes to the framework's server-error bus → the plugin captures it, tagged `voltro.errorSource` + `voltro.name` (+ traceId for workflows) |
|
|
@@ -1194,10 +1194,20 @@ const { status, reportSuccess } = useConnectionStatus('app')
|
|
|
1194
1194
|
`status` is `'connected' | 'degraded' | 'offline'`. `offline` is browser-reported
|
|
1195
1195
|
(`navigator.onLine` plus its `online` / `offline` events) — reliable for "the
|
|
1196
1196
|
network is gone", and it wins over `degraded`. `degraded` means an rpc on that
|
|
1197
|
-
api failed
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1197
|
+
api failed with `kind: 'transport'` and no later call has proved the server
|
|
1198
|
+
reachable. Declared handler errors do **not** colour the connection; if already
|
|
1199
|
+
degraded, a handler reply clears it because the server demonstrably answered.
|
|
1200
|
+
Local client and unknown failures leave the connection state unchanged. Any rpc
|
|
1201
|
+
success also clears the failure count automatically. Coming back online clears
|
|
1202
|
+
it too, since failures counted during an offline window *are* that window.
|
|
1203
|
+
`reportSuccess()` remains the explicit escape hatch for another signal that
|
|
1204
|
+
proves a round trip succeeded.
|
|
1205
|
+
|
|
1206
|
+
Use this hook to **render aggregate connection state**. To classify the failure
|
|
1207
|
+
that is firing right now — for example, to avoid a data-error toast in the same
|
|
1208
|
+
tick the socket drops — branch on `event.kind` inside
|
|
1209
|
+
[`useOnRpcError`](/docs/ui/client-utilities/use-on-rpc-error). React state updates
|
|
1210
|
+
after the event and therefore cannot classify that event retroactively.
|
|
1201
1211
|
|
|
1202
1212
|
Also returned: `online`, `failureCount`, `lastFailureAt`. There is deliberately
|
|
1203
1213
|
no polling ping — the framework does not call the server just to colour an
|
|
@@ -1578,16 +1588,34 @@ import { useOnRpcError } from '@voltro/client'
|
|
|
1578
1588
|
import { errorTag } from '@voltro/protocol'
|
|
1579
1589
|
|
|
1580
1590
|
useOnRpcError('app', useCallback((e) => {
|
|
1591
|
+
if (e.kind === 'transport') {
|
|
1592
|
+
showConnectionLost()
|
|
1593
|
+
return
|
|
1594
|
+
}
|
|
1581
1595
|
if (errorTag(e.error) === 'Unauthenticated') redirectToSignIn()
|
|
1582
1596
|
}, []))
|
|
1583
1597
|
```
|
|
1584
1598
|
|
|
1585
|
-
The event is `{ source, tag, error, traceId? }`. `source` is
|
|
1599
|
+
The event is `{ source, tag, error, kind, traceId? }`. `source` is
|
|
1586
1600
|
`'mutation' | 'action' | 'subscription'`, so both unary calls and stream
|
|
1587
1601
|
failures arrive here. `error` is the raw value — a `Schema.TaggedError`, a plain
|
|
1588
1602
|
`Error`, or anything else that was thrown; `errorTag(err)` reads `_tag` off
|
|
1589
|
-
tagged errors and returns `undefined` otherwise.
|
|
1590
|
-
|
|
1603
|
+
tagged errors and returns `undefined` otherwise.
|
|
1604
|
+
|
|
1605
|
+
`kind` is `'transport' | 'handler' | 'client' | 'unknown'`:
|
|
1606
|
+
|
|
1607
|
+
- `transport` — @effect/rpc could not complete the exchange. Use this branch for
|
|
1608
|
+
connection banners and to suppress misleading data-error toasts.
|
|
1609
|
+
- `handler` — the rpc returned a declared failure, such as `Unauthenticated`.
|
|
1610
|
+
- `client` — framework work around the call failed locally.
|
|
1611
|
+
- `unknown` — a combined or opaque operation could not be classified honestly.
|
|
1612
|
+
|
|
1613
|
+
`traceId` identifies the **client call span**. When the server accepted the
|
|
1614
|
+
request, it propagates the same id and `voltro logs --trace <id>` can join the
|
|
1615
|
+
call to server logs. A transport error can happen before the server saw the
|
|
1616
|
+
request, or after it handled the request but before the response returned, so a
|
|
1617
|
+
`traceId` on `kind: 'transport'` is not proof that server logs exist. For the
|
|
1618
|
+
same reason the event deliberately has no `reachedServer` boolean.
|
|
1591
1619
|
|
|
1592
1620
|
Two things to know. The hook re-subscribes whenever the listener **reference**
|
|
1593
1621
|
changes, so define it with `useCallback` or at module scope unless you want that.
|
|
@@ -1598,9 +1626,9 @@ per api name.
|
|
|
1598
1626
|
is also published on the client error bus, so a reporter — the Sentry browser
|
|
1599
1627
|
integration, or anything wired with `subscribeClientErrors` — already sees it,
|
|
1600
1628
|
under `source: 'rpc.mutation' | 'rpc.action' | 'rpc.subscription'` with the rpc
|
|
1601
|
-
tag and `traceId` in `context`. This hook is for
|
|
1602
|
-
on `Unauthenticated`, toast on a network
|
|
1603
|
-
the two by hand now reports twice.
|
|
1629
|
+
tag, `rpcErrorKind`, and optional `traceId` in `context`. This hook is for
|
|
1630
|
+
cross-cutting *policy* (redirect on `Unauthenticated`, toast on a network
|
|
1631
|
+
failure), not for telemetry; bridging the two by hand now reports twice.
|
|
1604
1632
|
|
|
1605
1633
|
Everything is published, including a reconnect storm. If that is too loud for
|
|
1606
1634
|
your reporter, gate it in the subscriber — which failures are worth an event is
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# What's new in 0.
|
|
1
|
+
# What's new in 0.61.0
|
|
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
|
|
@@ -9,51 +9,21 @@ BREAKING entries name a codemod; run `voltro update` to apply it.
|
|
|
9
9
|
|
|
10
10
|
### ⚠ BREAKING
|
|
11
11
|
|
|
12
|
-
- **@voltro/cli** —
|
|
12
|
+
- **@voltro/client, @voltro/web, @voltro/cli** — `RpcError.kind` now distinguishes transport, handler, client, and unknown failures; manually emitted events must add the field, while `useConnectionStatus` now degrades only for transport failures and clears on the next rpc success.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**`voltro update` carries you across this** — codemod `0.61.0/01_rpc_errors_have_kinds`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.61.0).
|
|
15
|
+
- **@voltro/plugin-sentry, @voltro/cli** — Browser RPC spans now join the active page/navigation transaction and link to the Effect/server trace instead of creating one root transaction per call; `tracesSampleRate`, `browserTracing`, and the new `rpcSpans` switch control the resulting browser trace volume.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
**`voltro update` carries you across this** — codemod `0.61.0/02_sentry_rpc_spans_join_page`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.61.0).
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Three details that come with it:
|
|
21
|
-
|
|
22
|
-
- **The old spelling is REFUSED, not ignored.** A moved option that is silently dropped goes green, stops emitting maps, and is found by someone reading an unresolvable frame months later. - **Each output directory uploads under the prefix that matches how its files are named** — `~/assets` for the browser bundle, `~/` for the bundles a node process loaded. One prefix across both is wrong for one of them. - **The upload and sweep run at every exit of the build**, not after the client bundle. Sweeping there would have deleted the client maps before the server ones existed, leaving those neither uploaded nor deleted.
|
|
23
|
-
|
|
24
|
-
`minifiedBundlesCarryMaps.test.ts` asserts the property over the SET: every bundler options object in the CLI that turns minification on must also decide `sourcemap`. That is what was missing — not one option, three, because nothing was watching the class.
|
|
25
|
-
|
|
26
|
-
**`voltro update` carries you across this** — codemod `0.59.0/01_sourcemaps_moved_to_the_top_level`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.59.0).
|
|
27
|
-
- **@voltro/ai** — **Speech has a cost path, and a built-in provider name can be taken over.**
|
|
28
|
-
|
|
29
|
-
Three changes, one thread: the speech seam existed and nothing could reach it.
|
|
30
|
-
|
|
31
|
-
**`registerSpeechProvider(name, provider, { replaceBuiltin: true })`.** Refusing `mock` / `openai` / `gateway` / `anthropic` outright was wrong, and the case that shows it is one the refusal could not see: a provider name is not always an alias the app chooses. `openai` is the segment the gateway puts in its model ids, so it is what a stored catalog's rows carry and what a picker submits — an app told to pick another name cannot, without rewriting persisted rows and live sessions. It keeps its own dispatch instead, for every provider rather than the colliding one. What the refusal protected is kept by putting the intent in the call.
|
|
32
|
-
|
|
33
|
-
**Registered providers resolve BEFORE built-ins.** The dispatch decided from a hard-coded list of built-in names, so without this the flag would let you register `openai` and never route to it — declared, documented, wired at zero call sites.
|
|
34
|
-
|
|
35
|
-
**`MeteredUsage` — a tally in characters or seconds.** Every speech vendor meters one of those; none meters tokens. A ledger that only knew tokens could not hold a speech call at all, so speech spend was not un-priced, it was *absent*. The token shape is untouched (it is what the AI SDK returns); the new shape is separate rather than a unit tacked onto `AiUsage`, because writing a character count into a field called `inputTokens` puts a lie in a row someone reads later.
|
|
36
|
-
|
|
37
|
-
`_voltro_ai_usage` gains a `unit` column, and `costSource` gains `'unpriced'`: a non-token unit with no supplied price is written with the real count and a zero cost rather than a fabricated one, since a per-1M-token rate applied to a character count produces a number indistinguishable from a real one. Sum the count columns grouped by `unit`, never across it.
|
|
38
|
-
|
|
39
|
-
BREAKING for a caller that had narrowed `CostBreakdown['costSource']` to two values or reads `estimateCostUsd`'s return positionally. No call site changes SHAPE — the token path takes the same arguments and returns the same numbers — but a narrowing is user-authored code, so this carries a `manual` codemod rather than `none`: nothing here can be transformed (whether the third member belongs in your union is your decision), and the reader who needs it is exactly the one a "touches no user-authored code" line would have sent away. It also names the query that does not fail but answers wrongly: summing `_voltro_ai_usage` counts ACROSS units now adds characters to tokens.
|
|
40
|
-
|
|
41
|
-
**`voltro update` carries you across this** — codemod `0.59.0/02_costsource_gained_unpriced`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.59.0).
|
|
42
|
-
|
|
43
|
-
### Added
|
|
44
|
-
|
|
45
|
-
- **@voltro/ai** — **`GatewayModelInfo.raw`** — the `/v1/models` entry exactly as it came over the wire, beside the normalised fields.
|
|
46
|
-
|
|
47
|
-
Normalising is the right default: the union of `tags`, `supported_parameters` and `modalities` is framework knowledge, and every app on the gateway was writing those forty lines differently. But normalising is lossy by construction, and the fields it drops are not decoration — a picker built on the vendor's own capability tags, a pricing display that shows a structure we have not normalised, `zdr` and `no_training` kept apart instead of folded into `dataPolicy`.
|
|
48
|
-
|
|
49
|
-
Without `raw`, such an app fetches the endpoint a second time and maintains a second parser of the same response. One field rather than three more normalised ones, because the alternative asks the same question again at every field the gateway adds.
|
|
19
|
+
### Fixed
|
|
50
20
|
|
|
51
|
-
|
|
21
|
+
- **@voltro/cli** — **Six SSR failure sites report to Sentry, not three.**
|
|
52
22
|
|
|
53
|
-
|
|
23
|
+
0.60.0 gave the web server its own Sentry and wired the three loud sites: the SSR shell throw, the SPA layout shell throw, and the general render error (which covers a loader throw). The docs said "shell throw, loader throw, PPR/SWR refresh" — and an enumeration in a claim is read as exhaustive.
|
|
54
24
|
|
|
55
|
-
|
|
25
|
+
The other three were `warn`-level and reported to nobody: a failed PPR hole pass, a failed background SWR refresh, and a `not-found.tsx` that throws while rendering. They are quieter because the request still serves something — stale HTML, an unfilled hole, a plain 404 — which says something about the REQUEST and nothing about who else could find out. The answer to that is nobody: none of them reaches a browser boundary, so the pod's stdout was the only record.
|
|
56
26
|
|
|
57
|
-
|
|
27
|
+
They carry their own `voltro.stage` (`not-found-render`, `ppr-holes`, `swr-refresh`), so a quota-conscious project can drop them by stage without losing the three that fail the request.
|
|
58
28
|
|
|
59
|
-
|
|
29
|
+
`webSentry.test.ts` now fails if a render-path `log.error`/`log.warn` gains no reporter beside it — the property, rather than the six call sites.
|