@voltro/cli 0.42.0 → 0.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +160 -0
- package/dist/apiBuild-B7NgSMhX.js +2 -0
- package/dist/{apiBuild-B_iH24qf.js → apiBuild-DF0f69Gf.js} +2 -2
- package/dist/bin.js +1 -1
- package/dist/{build-DUdi6F_Q.js → build-LC_IJaHL.js} +7 -7
- package/dist/{checkCommand-B2RWrpWi.js → checkCommand-DBpTSK8d.js} +1 -1
- package/dist/{checkCommand-DWL2_PPR.js → checkCommand-DDxwJCja.js} +2 -2
- package/dist/{codegenCommand-DkjoJ30Q.js → codegenCommand-LjxfK4Am.js} +2 -2
- package/dist/{codemodRunner-sFuWDETH.js → codemodRunner-Cnnz4-I7.js} +19 -4
- package/dist/{commands-DoTClnSk.js → commands-CbQcuWsE.js} +20 -20
- package/dist/{dashboardCommand-Bro7Jwts.js → dashboardCommand-CAHjCe_9.js} +3 -3
- package/dist/{dataCommand-Rj57y1vg.js → dataCommand-CrqvbVOv.js} +143 -133
- package/dist/{dev-D4l_YOzI.js → dev-B2rZfqxw.js} +2 -2
- package/dist/{dev-DuqBJC7g.js → dev-D0E5x2Ue.js} +1 -1
- package/dist/doctorCommand-CdsctSwg.js +2 -0
- package/dist/{doctorCommand-TmZTAepc.js → doctorCommand-Ct_3BHgd.js} +226 -216
- package/dist/{dormancyCommand-CUKhRmGG.js → dormancyCommand-D1DKnYce.js} +1 -1
- package/dist/{embeddingsCommand-Db-iQM3l.js → embeddingsCommand-1sZRBKlM.js} +1 -1
- package/dist/{envCommand-DQ_URZq9.js → envCommand-7qjlQgLD.js} +2 -2
- package/dist/{evolveCommand-CghRPrGO.js → evolveCommand-D5opB48I.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BCs_ZFnl.js → infoCommand-Bs_8RU22.js} +1 -1
- package/dist/{runtimeTrace-jFKvOPBz.js → runtimeTrace-Wmi7V-wN.js} +1 -1
- package/dist/{sdkgen-COQqHB9V.js → sdkgen-DbTFGCjg.js} +1 -1
- package/dist/{serveCommand-C512kzZA.js → serveCommand-C8xzYCwa.js} +4 -4
- package/dist/serveCommand-CGNTXafv.js +2 -0
- package/dist/serveEntry.js +1 -1
- package/dist/{start-fifc0xpW.js → start-BAlGcjF5.js} +1 -1
- package/dist/{start-BjcuFME_.js → start-Ds3xyn8R.js} +1 -1
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +1 -1
- package/dist/updateCommand-BvkKlV7D.js +2 -0
- package/dist/{updateCommand-HZQVBU3T.js → updateCommand-CrI9B4d7.js} +1 -1
- package/dist/{webDev-Dk8lFUzf.js → webDev-101csg6Q.js} +2 -2
- package/dist/webDev-BLKBO34K.js +2 -0
- package/dist/{webhooksCommand-D7ZpH7w0.js → webhooksCommand-BXUP5YjW.js} +1 -1
- package/package.json +23 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/authentication.md +1 -1
- package/templates/agent-docs/cli.md +2 -2
- package/templates/agent-docs/data.md +14 -12
- package/templates/agent-docs/database/migrations.md +1 -1
- package/templates/agent-docs/internationalization.md +2 -2
- package/templates/agent-docs/observability.md +1 -1
- package/templates/agent-docs/plugins/openapi.md +1 -1
- package/templates/agent-docs/plugins/versioning.md +1 -1
- package/templates/agent-docs/plugins/webhooks.md +1 -1
- package/templates/agent-docs/plugins.md +1 -1
- package/templates/agent-docs/routing.md +1 -1
- package/templates/agent-docs/scheduling.md +1 -1
- package/templates/agent-docs/security.md +2 -2
- package/templates/agent-docs/templates/apibackends.md +1 -1
- package/templates/agent-docs/whats-new.md +38 -66
- 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-sqlite/package.json +8 -8
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/package.json +10 -10
- 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-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-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +9 -9
- package/templates/apps/frontend-auth/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/package.json +9 -9
- package/templates/apps/frontend-collab/package.json +10 -10
- 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-portal/package.json +8 -8
- package/templates/apps/frontend-saas/package.json +8 -8
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/package.json +8 -8
- package/templates/apps/mobile-app/package.json +4 -4
- package/dist/apiBuild-RM5W5YXk.js +0 -2
- package/dist/doctorCommand-Bw3V4L1j.js +0 -2
- package/dist/serveCommand-gj7MsqXY.js +0 -2
- package/dist/updateCommand-B2alql9k.js +0 -2
- package/dist/webDev-D6QeXw3P.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as e } from "./dev-
|
|
2
|
-
import { _t as t, gt as n, mt as r, pt as i } from "./webDev-
|
|
1
|
+
import { c as e } from "./dev-B2rZfqxw.js";
|
|
2
|
+
import { _t as t, gt as n, mt as r, pt as i } from "./webDev-101csg6Q.js";
|
|
3
3
|
import { join as a } from "node:path";
|
|
4
4
|
import { promises as o } from "node:fs";
|
|
5
5
|
import { createLogger as s } from "@voltro/logger";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as e } from "./cliOutput-D1tSBoRM.js";
|
|
2
|
-
import { t } from "./checkCommand-
|
|
3
|
-
import { i as n, n as r } from "./codemodRunner-
|
|
2
|
+
import { t } from "./checkCommand-DDxwJCja.js";
|
|
3
|
+
import { i as n, n as r } from "./codemodRunner-Cnnz4-I7.js";
|
|
4
4
|
import { basename as i, join as a, resolve as o } from "node:path";
|
|
5
5
|
import { branchNamespaceName as s, planBranchProvision as c } from "@voltro/database";
|
|
6
6
|
import { readFile as l } from "node:fs/promises";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as e, r as t, t as n } from "./cliOutput-D1tSBoRM.js";
|
|
2
|
-
import { o as r } from "./updateCommand-
|
|
2
|
+
import { o as r } from "./updateCommand-CrI9B4d7.js";
|
|
3
3
|
import { join as i, resolve as a } from "node:path";
|
|
4
4
|
import { readFileSync as o, readdirSync as s, statSync as c } from "node:fs";
|
|
5
5
|
//#region src/infoCommand.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e } from "./apiBuild-
|
|
1
|
+
import { n as e } from "./apiBuild-DF0f69Gf.js";
|
|
2
2
|
import { join as t, resolve as n, sep as r } from "node:path";
|
|
3
3
|
import { existsSync as i, promises as a } from "node:fs";
|
|
4
4
|
import { createLogger as o } from "@voltro/logger";
|
|
@@ -422,7 +422,7 @@ let package = Package(
|
|
|
422
422
|
await l.mkdir(o(e), { recursive: !0 }), await l.writeFile(e, n.content, "utf8");
|
|
423
423
|
}
|
|
424
424
|
}, X = async (e) => {
|
|
425
|
-
let { offlineManifest: t } = await import("./checkCommand-
|
|
425
|
+
let { offlineManifest: t } = await import("./checkCommand-DBpTSK8d.js"), { manifest: n } = await t(e);
|
|
426
426
|
return n;
|
|
427
427
|
}, Z = async (e) => {
|
|
428
428
|
let t = J(await (e.manifestProvider ?? X)(e.root), {
|
|
@@ -3,10 +3,10 @@ import { t } from "./frameworkInspectState-CX2250XB.js";
|
|
|
3
3
|
import { r as n, t as r } from "./appModuleLoader-C9r9mxZt.js";
|
|
4
4
|
import { t as i } from "./bootRefusal-pgvDxlrU.js";
|
|
5
5
|
import { n as a, r as o } from "./startupRunner-CEqQq7ax.js";
|
|
6
|
-
import { $ as s, $t as ee, A as c, At as l, B as u, C as te, Ct as d, D as ne, Dt as f, E as re, Et as p, F as ie, Ft as m, G as h, Gt as ae, H as oe, I as se, It as ce, J as le, Jt as g, K as ue, Kt as _, L as de, Lt as fe, M as pe, Mt as v, N as me, Nt as he, O as ge, Ot as _e, P as ve, Pt as ye, Q as be, Qt as xe, R as Se, Rt as y, S as Ce, Sn as we, St as Te, T as Ee, Tt as De, U as Oe, Ut as b, V as ke, Vt as Ae, W as je, Wt as Me, X as Ne, Xt as Pe, Y as Fe, Yt as Ie, Z as Le, Zt as x, _ as Re, _n as ze, _t as S, an as Be, at as C, b as Ve, bn as He, bt as Ue, c as We, cn as w, ct as Ge, dn as Ke, dt as qe, en as Je, et as Ye, f as T, fn as E, ft as Xe, g as Ze, gn as Qe, gt as $e, h as et, ht as tt, i as nt, in as rt, it, j as at, jt as ot, k as st, kt as ct, ln as D, lt, m as ut, mn as dt, mt as O, nn as k, nt as ft, o as A, on as pt, ot as mt, p as ht, pn as gt, pt as _t, q as vt, qt as yt, r as bt, rn as xt, rt as St, sn as Ct, st as wt, t as Tt, tn as Et, tt as Dt, u as Ot, un as kt, ut as At, v as jt, vn as j, vt as M, w as N, wt as Mt, x as Nt, xn as Pt, xt as Ft, y as It, yn as Lt, yt as Rt, zt } from "./dev-
|
|
6
|
+
import { $ as s, $t as ee, A as c, At as l, B as u, C as te, Ct as d, D as ne, Dt as f, E as re, Et as p, F as ie, Ft as m, G as h, Gt as ae, H as oe, I as se, It as ce, J as le, Jt as g, K as ue, Kt as _, L as de, Lt as fe, M as pe, Mt as v, N as me, Nt as he, O as ge, Ot as _e, P as ve, Pt as ye, Q as be, Qt as xe, R as Se, Rt as y, S as Ce, Sn as we, St as Te, T as Ee, Tt as De, U as Oe, Ut as b, V as ke, Vt as Ae, W as je, Wt as Me, X as Ne, Xt as Pe, Y as Fe, Yt as Ie, Z as Le, Zt as x, _ as Re, _n as ze, _t as S, an as Be, at as C, b as Ve, bn as He, bt as Ue, c as We, cn as w, ct as Ge, dn as Ke, dt as qe, en as Je, et as Ye, f as T, fn as E, ft as Xe, g as Ze, gn as Qe, gt as $e, h as et, ht as tt, i as nt, in as rt, it, j as at, jt as ot, k as st, kt as ct, ln as D, lt, m as ut, mn as dt, mt as O, nn as k, nt as ft, o as A, on as pt, ot as mt, p as ht, pn as gt, pt as _t, q as vt, qt as yt, r as bt, rn as xt, rt as St, sn as Ct, st as wt, t as Tt, tn as Et, tt as Dt, u as Ot, un as kt, ut as At, v as jt, vn as j, vt as M, w as N, wt as Mt, x as Nt, xn as Pt, xt as Ft, y as It, yn as Lt, yt as Rt, zt } from "./dev-B2rZfqxw.js";
|
|
7
7
|
import { n as Bt } from "./cliArgs-D4p8n7EE.js";
|
|
8
8
|
import { r as Vt } from "./appPort-B_HpJ_ck.js";
|
|
9
|
-
import { ht as Ht, pt as P } from "./webDev-
|
|
9
|
+
import { ht as Ht, pt as P } from "./webDev-101csg6Q.js";
|
|
10
10
|
import { r as F } from "./dialectDriver-czCHYpeH.js";
|
|
11
11
|
import { i as Ut, n as Wt } from "./connectionConfig-Bk9IC7D0.js";
|
|
12
12
|
import { c as Gt, n as Kt, o as qt } from "./seedRunner-IdHEprqf.js";
|
|
@@ -16,7 +16,7 @@ import { h as un, o as dn } from "./inspect-B7U7Cl_Z.js";
|
|
|
16
16
|
import { n as fn } from "./mssqlClusterPatch-_4cE_nun.js";
|
|
17
17
|
import { i as pn, n as mn, r as hn, t as gn } from "./webhookDiscovery-CphsQe59.js";
|
|
18
18
|
import { t as _n } from "./inspectMetrics-CfdKLh6t.js";
|
|
19
|
-
import { r as vn, u as yn } from "./apiBuild-
|
|
19
|
+
import { r as vn, u as yn } from "./apiBuild-DF0f69Gf.js";
|
|
20
20
|
import { t as bn } from "./bootTiming-BdyP9nYw.js";
|
|
21
21
|
import { t as xn } from "./dataProfile-Cm0YVKSy.js";
|
|
22
22
|
import { basename as Sn, join as Cn } from "node:path";
|
|
@@ -1702,7 +1702,7 @@ var ji = () => Math.max(500, Math.floor(Yr() * .6)), Mi = (e) => new Promise((t)
|
|
|
1702
1702
|
schemaRegistry: Ue,
|
|
1703
1703
|
tenantScopedTables: _.tenantScopedTables,
|
|
1704
1704
|
softDeleteScopedTables: _.softDeleteScopedTables,
|
|
1705
|
-
dataTransferSnapshot: Hn(x),
|
|
1705
|
+
dataTransferSnapshot: Hn(x, h),
|
|
1706
1706
|
dataTransferDialect: h,
|
|
1707
1707
|
...Gr === void 0 ? {} : { ensureTenantProvisioned: Gr.ensure },
|
|
1708
1708
|
...qr === void 0 ? {} : { resolveResidentStore: qr.resolveStore },
|
package/dist/serveEntry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as e } from "./loadEnv-D9nEOClM.js";
|
|
2
2
|
import { r as t } from "./appModuleLoader-C9r9mxZt.js";
|
|
3
3
|
import { i as n } from "./dialectDriver-czCHYpeH.js";
|
|
4
|
-
import { t as r } from "./serveCommand-
|
|
4
|
+
import { t as r } from "./serveCommand-C8xzYCwa.js";
|
|
5
5
|
export { e as loadDotEnv, t as registerAppModules, n as registerDriver, r as runServe };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as e, t } from "./nodeEnvironment-cGFAj1J8.js";
|
|
2
2
|
import { r as n } from "./appPort-B_HpJ_ck.js";
|
|
3
|
-
import { A as r, C as i, D as a, E as o, F as s, H as c, I as l, J as ee, L as u, M as d, N as f, O as te, P as p, R as m, S as h, T as ne, V as g, W as re, X as _, Y as v, Z as y, a as b, b as x, d as S, f as C, ht as w, i as ie, j as T, k as E, n as D, nt as O, q as k, s as ae, tt as A, vt as j, x as oe, y as M, z as se } from "./webDev-
|
|
3
|
+
import { A as r, C as i, D as a, E as o, F as s, H as c, I as l, J as ee, L as u, M as d, N as f, O as te, P as p, R as m, S as h, T as ne, V as g, W as re, X as _, Y as v, Z as y, a as b, b as x, d as S, f as C, ht as w, i as ie, j as T, k as E, n as D, nt as O, q as k, s as ae, tt as A, vt as j, x as oe, y as M, z as se } from "./webDev-101csg6Q.js";
|
|
4
4
|
import { o as N, t as P } from "./connectionConfig-Bk9IC7D0.js";
|
|
5
5
|
import { A as F, D as I, a as ce, h as le, j as ue, k as de } from "./inspect-B7U7Cl_Z.js";
|
|
6
6
|
import { a as fe, c as L, d as pe, f as R, l as me, p as he, u as ge } from "./renderModeScan-D7J1B7Kw.js";
|
package/dist/startEntry.js
CHANGED
package/dist/startup.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { SyncLogger } from '@voltro/logger';
|
|
|
22
22
|
* So the race protected the shape we discourage and penalised the shape we
|
|
23
23
|
* teach — and the penalised one includes `searchBackfill`, our own example,
|
|
24
24
|
* which awaits a full-table query plus an index backfill and is the likeliest
|
|
25
|
-
* thing in the box to exceed two seconds. Slow AND failing put a
|
|
25
|
+
* thing in the box to exceed two seconds. Slow AND failing put a deployment back
|
|
26
26
|
* in exactly the silent state this file was fixed for.
|
|
27
27
|
*
|
|
28
28
|
* 60 seconds because the number now has one job: turn "hangs forever" into
|
|
@@ -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-Cnnz4-I7.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";
|
|
@@ -401,7 +401,7 @@ __installPublicEnv(publicEnvValues)
|
|
|
401
401
|
" same version at release — so this combination was never built or tested",
|
|
402
402
|
" as a set. It is not a judgement about whether they are compatible.",
|
|
403
403
|
"",
|
|
404
|
-
" Nothing else will tell you. A
|
|
404
|
+
" Nothing else will tell you. A deployment carried a package two releases",
|
|
405
405
|
" behind through `tsc`, 19 317 tests, `voltro doctor` exit 0 in four apps",
|
|
406
406
|
" and three production builds, all green. None of those checks is ABOUT",
|
|
407
407
|
" version agreement.",
|
|
@@ -2326,7 +2326,7 @@ The wrapper is what carries \`match\`, and what lets \`voltro doctor\` report wh
|
|
|
2326
2326
|
" const published = document.documentElement.lang",
|
|
2327
2327
|
" if (published && I18N_SUPPORTED.includes(published)) return published",
|
|
2328
2328
|
" // No published locale (a page served by something else, or an",
|
|
2329
|
-
" // attribute a
|
|
2329
|
+
" // attribute a deployment overwrote): fall back to the cookie, which",
|
|
2330
2330
|
" // both halves can read, and never to a client-only signal.",
|
|
2331
2331
|
" return resolveLocale({",
|
|
2332
2332
|
" cookieHeader: document.cookie,",
|
|
@@ -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-D0E5x2Ue.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-il9ti-HE.js");
|
|
226
226
|
return r((await n(await t(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.43.1",
|
|
4
4
|
"description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -628,6 +628,12 @@
|
|
|
628
628
|
"title": "`middleware.ts` exports `defineMiddleware(...)` and can say where it runs",
|
|
629
629
|
"kind": "transform"
|
|
630
630
|
},
|
|
631
|
+
{
|
|
632
|
+
"version": "0.43.0",
|
|
633
|
+
"id": "0.43.0/01_dangling-reference-to-rows-refused",
|
|
634
|
+
"title": "`DanglingReferenceError` → `RowsRefusedError` (named for the outcome, not a guessed cause)",
|
|
635
|
+
"kind": "transform"
|
|
636
|
+
},
|
|
631
637
|
{
|
|
632
638
|
"version": "0.5.0",
|
|
633
639
|
"id": "0.5.0/01_one-terminal-exactly-one",
|
|
@@ -715,22 +721,22 @@
|
|
|
715
721
|
"@effect/platform-node": "^0.108.0",
|
|
716
722
|
"@effect/sql": "^0.52.0",
|
|
717
723
|
"@effect/workflow": "^0.19.0",
|
|
718
|
-
"@voltro/ai": "0.
|
|
719
|
-
"@voltro/cache": "0.
|
|
720
|
-
"@voltro/data-transfer": "0.
|
|
721
|
-
"@voltro/database": "0.
|
|
722
|
-
"@voltro/env": "0.
|
|
723
|
-
"@voltro/kv": "0.
|
|
724
|
-
"@voltro/logger": "0.
|
|
725
|
-
"@voltro/plugin-auth": "0.
|
|
726
|
-
"@voltro/plugin-broadcast": "0.
|
|
727
|
-
"@voltro/plugin-mail": "0.
|
|
728
|
-
"@voltro/plugin-storage": "0.
|
|
729
|
-
"@voltro/plugin-webhooks": "0.
|
|
730
|
-
"@voltro/protocol": "0.
|
|
731
|
-
"@voltro/runtime": "0.
|
|
732
|
-
"@voltro/serverless": "0.
|
|
733
|
-
"@voltro/workflow": "0.
|
|
724
|
+
"@voltro/ai": "0.43.1",
|
|
725
|
+
"@voltro/cache": "0.43.1",
|
|
726
|
+
"@voltro/data-transfer": "0.43.1",
|
|
727
|
+
"@voltro/database": "0.43.1",
|
|
728
|
+
"@voltro/env": "0.43.1",
|
|
729
|
+
"@voltro/kv": "0.43.1",
|
|
730
|
+
"@voltro/logger": "0.43.1",
|
|
731
|
+
"@voltro/plugin-auth": "0.43.1",
|
|
732
|
+
"@voltro/plugin-broadcast": "0.43.1",
|
|
733
|
+
"@voltro/plugin-mail": "0.43.1",
|
|
734
|
+
"@voltro/plugin-storage": "0.43.1",
|
|
735
|
+
"@voltro/plugin-webhooks": "0.43.1",
|
|
736
|
+
"@voltro/protocol": "0.43.1",
|
|
737
|
+
"@voltro/runtime": "0.43.1",
|
|
738
|
+
"@voltro/serverless": "0.43.1",
|
|
739
|
+
"@voltro/workflow": "0.43.1",
|
|
734
740
|
"chokidar": "^5.0.0",
|
|
735
741
|
"ioredis": "^5.11.1",
|
|
736
742
|
"tinyglobby": "^0.2.17",
|
package/templates/AGENTS.md
CHANGED
|
@@ -707,7 +707,7 @@ each plugin's own README.
|
|
|
707
707
|
|
|
708
708
|
| Topic | Open | Summary |
|
|
709
709
|
|---|---|---|
|
|
710
|
-
| **What's new in 0.
|
|
710
|
+
| **What's new in 0.43.1** | `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. |
|
|
711
711
|
| 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. |
|
|
712
712
|
| 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. |
|
|
713
713
|
| 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. |
|
|
@@ -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.43.1** | `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. |
|
|
@@ -2166,7 +2166,7 @@ enforcing nothing while looking like it does.
|
|
|
2166
2166
|
**A guard naming an unregistered `resourceType` refuses the BOOT.** Denying is
|
|
2167
2167
|
correct per call and useless as a deployment outcome: the app comes up green and
|
|
2168
2168
|
every guarded procedure is down, with a log line per refused call as the only
|
|
2169
|
-
sign. A
|
|
2169
|
+
sign. A deployment put a number on it — 39 procedures, team settings through role
|
|
2170
2170
|
administration. So the two facts are compared once, after the startups have run,
|
|
2171
2171
|
and a missing registration names the type and the procedures that demanded it.
|
|
2172
2172
|
The commonest cause is a typo: the `resourceType` in a guard and the one in
|
|
@@ -1690,7 +1690,7 @@ It covers both halves of the stack:
|
|
|
1690
1690
|
| client | `data === undefined` / `!data` on a subscription result | branch on `loading` (and `idle`, if you pass `skip`) |
|
|
1691
1691
|
|
|
1692
1692
|
The subscription rule **resolves the binding** rather than matching text, and
|
|
1693
|
-
that distinction is the reason this scanner parses at all. A
|
|
1693
|
+
that distinction is the reason this scanner parses at all. A deployment migrating
|
|
1694
1694
|
these call sites wrote a regex codemod for the same job, and it rewrote a
|
|
1695
1695
|
`summary === undefined` check inside a child component where `summary` was a
|
|
1696
1696
|
PROP. Their compiler happened to catch it, because that name was out of scope
|
|
@@ -3777,7 +3777,7 @@ you ran it keeps the module graph it loaded at boot, so it goes on executing the
|
|
|
3777
3777
|
previous version indefinitely — and against a `.framework` directory that has
|
|
3778
3778
|
since been rewritten.
|
|
3779
3779
|
|
|
3780
|
-
That mix is worse than either version alone. A
|
|
3780
|
+
That mix is worse than either version alone. A deployment lost half an hour to a
|
|
3781
3781
|
pod whose api had started before the upgrade: it served requests, reported
|
|
3782
3782
|
healthy, and returned no SSR at all, because the running process held the old
|
|
3783
3783
|
modules while the build output on disk was new.
|
|
@@ -275,7 +275,7 @@ fails the call with a `ParseError` naming the key and listing what was expected:
|
|
|
275
275
|
```
|
|
276
276
|
|
|
277
277
|
It used to be discarded silently, and the reason that is worse than it sounds is
|
|
278
|
-
what a discarded FILTER means. A
|
|
278
|
+
what a discarded FILTER means. A deployment's layout called a `userSettings.list`
|
|
279
279
|
that declares `userId` with `{ employeeId }`; the payload decoded to `{}`, which
|
|
280
280
|
for a list query is not a narrower filter but the absence of one, and an admin
|
|
281
281
|
was served another user's row.
|
|
@@ -2294,7 +2294,7 @@ export default (input, ctx) => Effect.gen(function* () {
|
|
|
2294
2294
|
|
|
2295
2295
|
**Inside a mutation it fires on COMMIT, and not at all on rollback.** That is not a nicety: a display reacting to a game start the database rolled back happens on every constraint violation, every deadlock retry, every guard that fails *after* the publish line. Outside a transaction it fires immediately.
|
|
2296
2296
|
|
|
2297
|
-
Three typed errors reach the **producer**, so a mismatch is one failing call rather than every
|
|
2297
|
+
Three typed errors reach the **producer**, so a mismatch is one failing call rather than every deployment's handler breaking on a field that is not there: `EventPayloadInvalid`, `EventKeyInvalid`, `EventPayloadTooLarge`.
|
|
2298
2298
|
|
|
2299
2299
|
`EventPayloadTooLarge` fires at **7,500 bytes** for the encoded envelope (event
|
|
2300
2300
|
name + key + payload, as JSON). The ceiling is not arbitrary and it is not a
|
|
@@ -2631,7 +2631,7 @@ delivery matters drops the ones in between; choosing `each` for a per-frame
|
|
|
2631
2631
|
stream makes a slow client work through a backlog to reach a state it could have
|
|
2632
2632
|
had immediately, and report a "loss" that was never one.
|
|
2633
2633
|
|
|
2634
|
-
The test: **would a
|
|
2634
|
+
The test: **would a deployment be wrong to miss one?**
|
|
2635
2635
|
|
|
2636
2636
|
<Callout type="warn">
|
|
2637
2637
|
`delivery: 'latest'` cannot be combined with `webhook`, and the declaration is
|
|
@@ -2654,7 +2654,7 @@ separate, coarser one for the outside world.
|
|
|
2654
2654
|
publishing at 10–20Hz is usually indistinguishable to a human and an order of
|
|
2655
2655
|
magnitude cheaper.
|
|
2656
2656
|
|
|
2657
|
-
The honest rule: **use an event when a
|
|
2657
|
+
The honest rule: **use an event when a deployment would be wrong to miss one.** If
|
|
2658
2658
|
missing one is fine because the next one supersedes it, either declare
|
|
2659
2659
|
`delivery: 'latest'` or model it as state — a table, or a value the client
|
|
2660
2660
|
replaces.
|
|
@@ -4756,23 +4756,25 @@ declared it anyway, that is harmless — the union is the same either way.
|
|
|
4756
4756
|
|
|
4757
4757
|
## Framework-shipped error variants
|
|
4758
4758
|
|
|
4759
|
-
`@voltro/protocol` exports
|
|
4759
|
+
`@voltro/protocol` exports the framework's own tagged errors. The rest of your typed errors are ones you declare yourself (above) or ones a plugin contributes.
|
|
4760
|
+
|
|
4761
|
+
**Import them from `@voltro/protocol`, not from `@voltro/runtime`** — even the store errors the runtime raises. A descriptor that declares one in `error:` is loaded value-level by the web client (the `RpcClient` needs every procedure's Schema), and `@voltro/runtime` is server-only, so a descriptor importing from it is refused at boot by the browser-safety guard. `@voltro/runtime` re-exports them for server code, which never sees the difference.
|
|
4760
4762
|
|
|
4761
4763
|
| Variant | From | When |
|
|
4762
4764
|
|---|---|---|
|
|
4763
4765
|
| `ScopeError` | `@voltro/protocol` | `requireScope(subject, scope)` failed — `{ required, message }`. |
|
|
4764
4766
|
| `Unauthenticated` | `@voltro/protocol` | The resolved Subject is anonymous but a signed-in caller was required — optional `{ reason }`. |
|
|
4765
|
-
| `TenantScopeViolation` | `@voltro/
|
|
4766
|
-
| `TenantRowNotFound` | `@voltro/
|
|
4767
|
-
| `ServerOnlyColumnWrite` | `@voltro/
|
|
4768
|
-
| `StoreOperationFailed` | `@voltro/
|
|
4769
|
-
| `TableValidationFailed` | `@voltro/
|
|
4770
|
-
| `ConstraintViolation` | `@voltro/
|
|
4767
|
+
| `TenantScopeViolation` | `@voltro/protocol` | A tenant-scoped `EffectStore` write had no authenticated subject. |
|
|
4768
|
+
| `TenantRowNotFound` | `@voltro/protocol` | A keyed-by-id write (`store.update(t, id, …)`, `delete`, `hardDelete`, `patchJson`) on a `tenant()` table found no such row **in the caller's tenant**. Raised identically whether the row is missing or belongs to another tenant — the distinction would be a cross-tenant existence oracle. |
|
|
4769
|
+
| `ServerOnlyColumnWrite` | `@voltro/protocol` | A `crud.create` / `crud.update` input tried to set a [`.serverOnly()`](/docs/database/sensitivity) column — `{ table, columns }`. |
|
|
4770
|
+
| `StoreOperationFailed` | `@voltro/protocol` | The underlying store operation failed (transient). |
|
|
4771
|
+
| `TableValidationFailed` | `@voltro/protocol` | A `table().validate(Schema)` row check rejected the write. |
|
|
4772
|
+
| `ConstraintViolation` | `@voltro/protocol` | The database refused the write on a foreign key / unique / NOT NULL / CHECK — `{ kind, table, operation, constraint?, column? }`. See below. |
|
|
4771
4773
|
| `CacheError` | `@voltro/cache` | A cache backend op failed — `{ operation, key, cause }`. |
|
|
4772
4774
|
| `RateLimited` | `@voltro/plugin-ratelimit` | The limiter rejected the call — `{ limit, retryAfterMs, resetAtMs }`. |
|
|
4773
4775
|
| `TenantMismatch` | `@voltro/plugin-multitenancy` | `assertOwnTenant(input.tenantId, subject)` rejected a cross-tenant write. |
|
|
4774
4776
|
|
|
4775
|
-
Each plugin that ships an error (`RateLimited`, `EntitlementExceeded`, `StorageError`, `MailError`, …) merges it into every procedure's wire-error union, so the client decodes it typed without you adding it to each `error:`. To surface a
|
|
4777
|
+
Each plugin that ships an error (`RateLimited`, `EntitlementExceeded`, `StorageError`, `MailError`, …) merges it into every procedure's wire-error union, so the client decodes it typed without you adding it to each `error:`. To surface a store error to the client, add it to the descriptor's `error:` union yourself (e.g. `error: Schema.Union(TenantScopeViolation, StoreOperationFailed, MyDomainError)`).
|
|
4776
4778
|
|
|
4777
4779
|
All carry `_tag` + typed payloads, all narrow correctly on the client.
|
|
4778
4780
|
|
|
@@ -1707,7 +1707,7 @@ The cloud dashboard surfaces per-env state with a multi-env tab in the [cloud UI
|
|
|
1707
1707
|
|
|
1708
1708
|
## Rehearsing a migration against real data
|
|
1709
1709
|
|
|
1710
|
-
The strongest check on this pipeline is not that each command exits 0 — it is that **no row moved that you did not ask to move**.
|
|
1710
|
+
The strongest check on this pipeline is not that each command exits 0 — it is that **no row moved that you did not ask to move**. This loop was built for a MariaDB cutover and caught three defects the framework's own suite did not, which is why it is written up here.
|
|
1711
1711
|
|
|
1712
1712
|
1. **Restore a backup into a throwaway database.**
|
|
1713
1713
|
|
|
@@ -91,7 +91,7 @@ import { LOCALE_COOKIE, THEME_COOKIE } from '@voltro/i18n'
|
|
|
91
91
|
|
|
92
92
|
Import them rather than retyping `'voltro:locale'`. A cookie name the framework READS and your app WRITES is a public API, and it is the only kind where both sides can disagree without anything failing: nothing throws, no page breaks, the resolver simply finds nothing and falls back to `Accept-Language`. The symptom is a preference that stops working for the subset of users whose browser language differs from their choice — the least likely thing anyone tests.
|
|
93
93
|
|
|
94
|
-
> Until 0.31.0 the only package exporting these was `@voltro/ui-shadcn`, and `voltro doctor` told you to import from there. An app on this package and not on the shadcn kit had no constant to reach for, and would have had to adopt a UI kit for two strings. Reported by a
|
|
94
|
+
> Until 0.31.0 the only package exporting these was `@voltro/ui-shadcn`, and `voltro doctor` told you to import from there. An app on this package and not on the shadcn kit had no constant to reach for, and would have had to adopt a UI kit for two strings. Reported by a deployment, who added that the rule "does not fire for us, and we think that is correct-by-accident".
|
|
95
95
|
|
|
96
96
|
## `Could not find required 'intl' object` during SSR
|
|
97
97
|
|
|
@@ -106,7 +106,7 @@ holds 2 live copies of @voltro/i18n bound to
|
|
|
106
106
|
2 distinct react-intl instances.
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
**Do not use `pnpm ls` to rule this out.** It enumerates versions ON DISK; the failure is module INSTANCES in a running process, and one file loaded down two paths is two instances. A
|
|
109
|
+
**Do not use `pnpm ls` to rule this out.** It enumerates versions ON DISK; the failure is module INSTANCES in a running process, and one file loaded down two paths is two instances. A deployment reported a 500 on every SSR page with exactly one version of each installed — they were right, and the check we had published could not observe the cause. If the count says one of each, it is a framework bug and the diagnosis says so.
|
|
110
110
|
|
|
111
111
|
The framework keeps both packages on one SSR instance by bundling them together (`ssr.noExternal`) and deduping them, in `voltro dev`, `voltro build` and `voltro start` alike. The remaining way to get a second copy is in your own code: building a provider from `react-intl` **directly** rather than from `@voltro/i18n`.
|
|
112
112
|
|
|
@@ -227,7 +227,7 @@ The in-memory ring always keeps EVERY span for live debugging. SEPARATELY, on po
|
|
|
227
227
|
- **Default OFF in production, `interesting` in dev.** `voltro dev` (not `NODE_ENV=production`) persists only "interesting" spans. `voltro serve` / `voltro start` default `NODE_ENV=production` when it's unset, so they persist NOTHING and **`_voltro_traces` is not even created** — use OTLP in prod. An explicit `NODE_ENV` is never overridden.
|
|
228
228
|
- **`VOLTRO_TRACING_PERSIST`** = `off` | `errors` | `interesting` | `all` (overrides the env default everywhere). `interesting` = error + slow + per-trace root spans, MINUS the high-volume subscription delivery (snapshot/delta) spans, MINUS the framework's own background-task reads. `errors` = error spans only. `all` = every non-delivery span. `off` = no persistence and no table.
|
|
229
229
|
|
|
230
|
-
**Why background tasks are excluded from "slow".** The framework polls four of its own tables on a timer (`_voltro_schedule_claims`, `_voltro_workflow_pending`, `_voltro_workflow_pauses`, `_voltro_ai_inferences`). Those reads are slow exactly when the database is under pressure — which is when persisting them costs the most. A
|
|
230
|
+
**Why background tasks are excluded from "slow".** The framework polls four of its own tables on a timer (`_voltro_schedule_claims`, `_voltro_workflow_pending`, `_voltro_workflow_pauses`, `_voltro_ai_inferences`). Those reads are slow exactly when the database is under pressure — which is when persisting them costs the most. A deployment measured the loop closing on itself: `_voltro_traces` at 476 571 rows / 335 MB, writing ~11 INSERTs/s onto the same 15-slot pooler the app read through, 99 % of it framework poller spans. Pool pressure makes the spans slow, slow spans are "interesting", persisting them costs pool. They are still kept when they ERROR, and `all` mode still keeps everything. A request-path framework table like `_voltro_api_keys` is NOT excluded — a slow lookup there is a real user waiting.
|
|
231
231
|
- **`VOLTRO_TRACING_SLOW_MS`** (default `500`) — the "slow" threshold used by `interesting`.
|
|
232
232
|
- **`VOLTRO_TRACING_PERSIST_DELIVERY`** (default off) — also persist the subscription delivery spans (the firehose; rarely wanted).
|
|
233
233
|
- **`VOLTRO_TRACING_SAMPLE`** (`0`–`1`, default `1`) — per-*trace* sampling (a kept trace keeps all its eligible spans; errors are never sampled out). `=0` with no explicit mode means off.
|
|
@@ -73,7 +73,7 @@ Per procedure kind:
|
|
|
73
73
|
- **stream** → the `element` Schema is documented as a streaming response (`application/x-ndjson`, one element per line) — **not** faked as a single JSON value.
|
|
74
74
|
- The procedure's **typed error channel** (its `Schema.TaggedError` union) becomes a `422` response referencing the error schema; a procedure with no declared error documents only `200` + `500`.
|
|
75
75
|
|
|
76
|
-
Mixing rpc + REST in one spec is fine — the rpc operations are grouped under the `rpc` tag and flagged `x-voltro-rpc`, so a
|
|
76
|
+
Mixing rpc + REST in one spec is fine — the rpc operations are grouped under the `rpc` tag and flagged `x-voltro-rpc`, so a deployment can tell them apart from real REST routes. `procedures` is opt-in: omit it to keep the spec REST-only.
|
|
77
77
|
|
|
78
78
|
## Access control
|
|
79
79
|
|
|
@@ -58,7 +58,7 @@ If 41 surprises you, `exclude` is the knob. The retention sweep (`VOLTRO_ROW_HIS
|
|
|
58
58
|
|
|
59
59
|
Versioning records **row changes, not domain events**. One entry per row per write, named by *table*. If your product has a user-facing audit feature whose entries are named after an aggregate root — one `Team` event for a call that writes `teams` + `roles` + `userTeams` + `userTeamRoles` — this is the layer **underneath** that, not a replacement for it.
|
|
60
60
|
|
|
61
|
-
The distinction is worth reading before you plan a migration onto it. A
|
|
61
|
+
The distinction is worth reading before you plan a migration onto it. A migration off hundreds of hand-written audit calls onto this tap runs into the same wall a few hours in: the grain is different. A table-keyed tap does not produce an aggregate-keyed trail with better coverage, it produces a *different artifact*. The two compose:
|
|
62
62
|
|
|
63
63
|
- **versioning** answers "what did row R look like before, and after" — for every write, whether or not anyone remembered to record it;
|
|
64
64
|
- an **aggregate trail** (the [audit sink](/docs/plugins/audit), one row per mutation invocation) answers "what business operation happened, to which entity, and did it succeed";
|
|
@@ -211,7 +211,7 @@ means equality; an object names an operator. `eq`, `in`, `gt`, `gte`, `lt`, `lte
|
|
|
211
211
|
— those six and nothing else. **An unsupported operator falls through to `===`
|
|
212
212
|
and therefore matches nothing.**
|
|
213
213
|
|
|
214
|
-
**This was typed `Readonly<Record<string, unknown>>` and it cost a
|
|
214
|
+
**This was typed `Readonly<Record<string, unknown>>` and it cost a deployment a
|
|
215
215
|
feature for a year.** They wrote in a code comment that the filter was key-path
|
|
216
216
|
equality and could not express "id is one of these", refused that capability in
|
|
217
217
|
their own API with a typed error, and shipped it — while `in` had been supported
|
|
@@ -1120,7 +1120,7 @@ store.onChange((event) => {
|
|
|
1120
1120
|
```
|
|
1121
1121
|
|
|
1122
1122
|
Why this exists: two framework tasks polled permanently-empty queues on a
|
|
1123
|
-
|
|
1123
|
+
deployment's deployment and wrote **2 506 claim rows an hour** between them,
|
|
1124
1124
|
against 18 from the app's own eight schedules. A fixed interval has no way to
|
|
1125
1125
|
learn that a queue is empty. Without a `wake()` source the ceiling is your
|
|
1126
1126
|
worst-case latency, so a task whose arrivals cannot announce themselves should
|
|
@@ -1058,7 +1058,7 @@ that look like they answer the same question do not:
|
|
|
1058
1058
|
branches on the ABSENCE OF A FUNCTION, which says nothing about why it is
|
|
1059
1059
|
absent and breaks the moment anything else becomes conditional.
|
|
1060
1060
|
- **`headers` is `{}` in the browser, not `undefined`** — so `if (ctx.headers)`
|
|
1061
|
-
is TRUE on both paths. A
|
|
1061
|
+
is TRUE on both paths. A deployment wrote exactly that check and it silently did
|
|
1062
1062
|
nothing.
|
|
1063
1063
|
|
|
1064
1064
|
```ts
|
|
@@ -363,7 +363,7 @@ Losers don't write a run row — at scale that would be N−1 noise rows per fir
|
|
|
363
363
|
and a plugin's over a framework default.
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
-
Through 0.32.0 this was a silent last-write-wins: a
|
|
366
|
+
Through 0.32.0 this was a silent last-write-wins: a deployment's 1-hour bound was replaced one second later by the framework's default, the startup went on logging `bounded to 1h` at every boot, and it was found by counting rows. Note the direction — the loser is chosen by WHO registered, not by which TTL is narrower. "Narrower wins" would let a framework default we tighten in a later release silently start deleting your data faster than you asked for.
|
|
367
367
|
|
|
368
368
|
## `cluster`
|
|
369
369
|
|
|
@@ -59,7 +59,7 @@ payload therefore answers with a decode error, not a `ScopeError` — and readin
|
|
|
59
59
|
that as "the guard is not applied" is the wrong conclusion in the dangerous
|
|
60
60
|
direction.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
That conclusion is easy to reach while auditing a guard, and flipped
|
|
63
63
|
back the moment they sent a complete payload. So a guarded procedure says it in
|
|
64
64
|
the failure:
|
|
65
65
|
|
|
@@ -107,7 +107,7 @@ yield* enrich(userSuppliedUrl).pipe(
|
|
|
107
107
|
|
|
108
108
|
Two things worth knowing if you are upgrading from a version before this was a failure:
|
|
109
109
|
|
|
110
|
-
- **It used to be a defect** (`Effect.die`), so code that handled it did so with `Effect.catchAllDefect`. That arm no longer fires. If you also have an `Effect.mapError` above it, the rejection now reaches `mapError` FIRST — and a delivery that classified an SSRF block as *terminal* in the defect arm will be reclassified as whatever `mapError` returns. A
|
|
110
|
+
- **It used to be a defect** (`Effect.die`), so code that handled it did so with `Effect.catchAllDefect`. That arm no longer fires. If you also have an `Effect.mapError` above it, the rejection now reaches `mapError` FIRST — and a delivery that classified an SSRF block as *terminal* in the defect arm will be reclassified as whatever `mapError` returns. A deployment hit exactly this: a permanent condition became retryable and burned the full backoff budget re-attempting a request that can never succeed. Nothing in the type system can see that, because the error channel already carried `HttpClientError`.
|
|
111
111
|
- **`description` names the policy** (`SSRF policy: blocked private/reserved address …`), which is what you want in a log line; the `cause` is what you want in a branch.
|
|
112
112
|
|
|
113
113
|
### Allowing a target on purpose
|
|
@@ -992,7 +992,7 @@ export default async ({ store, log, onShutdown, id }: StartupContext) => {
|
|
|
992
992
|
log.info(`startup '${id}': warming order-fulfillment caches`)
|
|
993
993
|
|
|
994
994
|
// Example long-lived resource: a heartbeat interval. Replace with a real
|
|
995
|
-
// warm-up (preload a dashboard cache, open a
|
|
995
|
+
// warm-up (preload a dashboard cache, open a deployment, …).
|
|
996
996
|
const timer = setInterval(() => {
|
|
997
997
|
void store // `store` is the already-migrated DataStore, ready to use.
|
|
998
998
|
}, 60_000)
|