@voltro/cli 0.49.0 → 0.50.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 +114 -0
- package/dist/apiBuild-BZ-plaQN.js +2 -0
- package/dist/{apiBuild-QmYHaCF4.js → apiBuild-D3s7lx57.js} +2 -2
- package/dist/appGraph-CM9wvUmD.js +2 -0
- package/dist/appGraph-Cv2W_wO5.js +111 -0
- package/dist/bin.js +1 -1
- package/dist/{build-Bi4O_g7T.js → build-CFmqF39c.js} +7 -7
- package/dist/{checkCommand-udbQpphy.js → checkCommand-DomvYDi1.js} +1 -1
- package/dist/{checkCommand-D8vjQXQt.js → checkCommand-DsKIjL6k.js} +54 -45
- package/dist/{codegenCommand-DApSH7hK.js → codegenCommand-C0S27nbB.js} +2 -2
- package/dist/{codemodRunner-De1_316I.js → codemodRunner-DwY8Rjbl.js} +696 -481
- package/dist/{commands-a5PxCaIg.js → commands-Brp4DI8u.js} +22 -22
- package/dist/{dashboardCommand-DaoUUqJS.js → dashboardCommand-bbLfc9FW.js} +3 -3
- package/dist/{dataCommand-CJv_OMoS.js → dataCommand-k9mXTXin.js} +511 -333
- package/dist/{dev-C31Nvlbv.js → dev-C0cbqysu.js} +4 -4
- package/dist/{dev-DFVuzOj7.js → dev-Dz9GqA6P.js} +1 -1
- package/dist/doctorCommand-BlciWUJb.js +2 -0
- package/dist/{doctorCommand-B3uraeAe.js → doctorCommand-DyViaaCP.js} +38 -22
- package/dist/{doctorSourceRelations-Dd48LPvO.js → doctorSourceRelations-c5mlN5HI.js} +66 -34
- package/dist/{dormancyCommand-QUXQ9yoz.js → dormancyCommand-DMZPA_HW.js} +1 -1
- package/dist/{embeddingsCommand-Zw1-rYSF.js → embeddingsCommand-CMxRPkg6.js} +1 -1
- package/dist/{envCommand-DtaJ4xEf.js → envCommand-CDLr-yD4.js} +2 -2
- package/dist/{evolveCommand-JDD3hE1i.js → evolveCommand-Ct4NesL3.js} +2 -2
- package/dist/{generateCommand-CcyvH2ve.js → generateCommand-BDdOxhnv.js} +1 -1
- package/dist/index.js +1 -1
- package/dist/{infoCommand-COsUC1DB.js → infoCommand-orsKJKUy.js} +1 -1
- package/dist/{inspect-B7U7Cl_Z.js → inspect-CtL_xTbu.js} +1 -0
- package/dist/inspect-CuGDYES0.js +2 -0
- package/dist/{interruptedReplace-C1ZI84xi.js → interruptedReplace-C3O3M1MM.js} +2 -1
- package/dist/interruptedReplace-CvmiAM9K.js +2 -0
- package/dist/manifestBuild-CPjhvM62.js +2 -0
- package/dist/{manifestBuild-BK42hu0k.js → manifestBuild-CuU1VrSm.js} +2 -2
- package/dist/{runtimeTrace-Di6fcndp.js → runtimeTrace-Bo8jWsNe.js} +1 -1
- package/dist/{sdkgen-CIi5gM17.js → sdkgen-OZhiT2vz.js} +1 -1
- package/dist/serveCommand-Bbgg7Paq.js +2 -0
- package/dist/{serveCommand-N3udT49p.js → serveCommand-YbD5VgfV.js} +8 -8
- package/dist/serveEntry.js +1 -1
- package/dist/{start-DvlYDyAr.js → start-7b_bL8Uc.js} +1 -1
- package/dist/{start-7HcSIKEp.js → start-B1nH9ahf.js} +2 -2
- package/dist/startEntry.js +1 -1
- package/dist/{updateCommand-2BfbeMvp.js → updateCommand-BgH6vTtq.js} +2 -1
- package/dist/updateCommand-BweY17z-.js +2 -0
- package/dist/{webDev-B4IEMB0n.js → webDev-BbdkzMbd.js} +1 -1
- package/dist/webDev-BcRFTsMA.js +2 -0
- package/dist/{webhooksCommand-CwX9IzvJ.js → webhooksCommand-DjE6u0r4.js} +1 -1
- package/package.json +41 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/cli.md +113 -3
- package/templates/agent-docs/data.md +36 -0
- package/templates/agent-docs/whats-new.md +30 -41
- 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-dZbHdqMg.js +0 -2
- package/dist/appGraph-KGDPTuTy.js +0 -110
- package/dist/appGraph-zuMGKVYX.js +0 -2
- package/dist/doctorCommand-CPL6EhrW.js +0 -2
- package/dist/inspect-DUze25t0.js +0 -2
- package/dist/interruptedReplace-DNJ7MKph.js +0 -2
- package/dist/manifestBuild-j0n109tt.js +0 -2
- package/dist/serveCommand-CzSDngKm.js +0 -2
- package/dist/updateCommand-CtYm7aKH.js +0 -2
- package/dist/webDev-B79ji8M5.js +0 -2
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { E as e, N as t, O as n, T as r, d as i, f as a, h as o, i as s, l as c, r as l, u } from "./fileConventions-Cof68_BL.js";
|
|
2
2
|
import { t as d } from "./pageConvention-cEiRxdab.js";
|
|
3
3
|
import { r as f } from "./tsconfigPaths-BWXBWgcl.js";
|
|
4
|
-
import { a as p, i as
|
|
5
|
-
import { basename as
|
|
6
|
-
import { existsSync as
|
|
7
|
-
import { execFileSync as
|
|
8
|
-
import { Node as
|
|
9
|
-
import { globSync as
|
|
4
|
+
import { a as p, i as m, t as h } from "./fileTaxonomy-CJfgOllU.js";
|
|
5
|
+
import { basename as g, dirname as _, join as v, relative as ee } from "node:path";
|
|
6
|
+
import { existsSync as y, readFileSync as b, statSync as te } from "node:fs";
|
|
7
|
+
import { execFileSync as ne } from "node:child_process";
|
|
8
|
+
import { Node as x, Project as re, QuoteKind as ie, SyntaxKind as S } from "ts-morph";
|
|
9
|
+
import { globSync as ae } from "tinyglobby";
|
|
10
10
|
//#region src/codemodHelpers.ts
|
|
11
|
-
var
|
|
11
|
+
var C = (e, t, n) => {
|
|
12
12
|
let r = [];
|
|
13
13
|
for (let i of e.getImportDeclarations()) if (i.getModuleSpecifierValue() === t) for (let e of i.getNamedImports()) {
|
|
14
14
|
if (e.getName() !== n) continue;
|
|
@@ -16,70 +16,70 @@ var x = (e, t, n) => {
|
|
|
16
16
|
r.push(t ? t.getText() : e.getName());
|
|
17
17
|
}
|
|
18
18
|
return r;
|
|
19
|
-
},
|
|
19
|
+
}, w = (e, t) => {
|
|
20
20
|
let n = e.getExpression();
|
|
21
|
-
return
|
|
22
|
-
},
|
|
21
|
+
return x.isIdentifier(n) && t.includes(n.getText());
|
|
22
|
+
}, oe = (e, { module: t, from: n, to: r }) => {
|
|
23
23
|
let i = 0;
|
|
24
24
|
for (let a of e.getSourceFiles()) for (let e of a.getImportDeclarations()) if (e.getModuleSpecifierValue() === t) for (let t of e.getNamedImports()) {
|
|
25
25
|
if (t.getName() !== n) continue;
|
|
26
26
|
let e = t.getNameNode();
|
|
27
|
-
!t.getAliasNode() &&
|
|
27
|
+
!t.getAliasNode() && x.isIdentifier(e) ? e.rename(r) : e.replaceWithText(r), i++;
|
|
28
28
|
}
|
|
29
29
|
return i;
|
|
30
|
-
},
|
|
30
|
+
}, se = (e, { from: t, to: n }) => {
|
|
31
31
|
let r = 0;
|
|
32
32
|
for (let i of e.getSourceFiles()) {
|
|
33
33
|
for (let e of i.getImportDeclarations()) e.getModuleSpecifierValue() === t && (e.setModuleSpecifier(n), r++);
|
|
34
34
|
for (let e of i.getExportDeclarations()) e.getModuleSpecifierValue() === t && (e.setModuleSpecifier(n), r++);
|
|
35
|
-
for (let e of i.getDescendantsOfKind(
|
|
36
|
-
if (e.getExpression().getKind() !==
|
|
35
|
+
for (let e of i.getDescendantsOfKind(S.CallExpression)) {
|
|
36
|
+
if (e.getExpression().getKind() !== S.ImportKeyword) continue;
|
|
37
37
|
let i = e.getArguments()[0];
|
|
38
|
-
i &&
|
|
38
|
+
i && x.isStringLiteral(i) && i.getLiteralValue() === t && (i.replaceWithText(JSON.stringify(n)), r++);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
return r;
|
|
42
|
-
},
|
|
42
|
+
}, ce = (e, { component: t, from: n, to: r }) => {
|
|
43
43
|
let i = 0;
|
|
44
44
|
for (let a of e.getSourceFiles()) {
|
|
45
|
-
let e = [...a.getDescendantsOfKind(
|
|
46
|
-
for (let a of e) if (a.getTagNameNode().getText() === t) for (let e of a.getAttributes())
|
|
45
|
+
let e = [...a.getDescendantsOfKind(S.JsxOpeningElement), ...a.getDescendantsOfKind(S.JsxSelfClosingElement)];
|
|
46
|
+
for (let a of e) if (a.getTagNameNode().getText() === t) for (let e of a.getAttributes()) x.isJsxAttribute(e) && e.getNameNode().getText() === n && (e.getNameNode().replaceWithText(r), i++);
|
|
47
47
|
}
|
|
48
48
|
return i;
|
|
49
|
-
},
|
|
49
|
+
}, le = (e, { inCallTo: t, module: n, from: r, to: i }) => {
|
|
50
50
|
let a = 0;
|
|
51
51
|
for (let o of e.getSourceFiles()) {
|
|
52
|
-
let e =
|
|
53
|
-
if (e.length !== 0) for (let t of o.getDescendantsOfKind(
|
|
54
|
-
if (!
|
|
52
|
+
let e = C(o, n, t);
|
|
53
|
+
if (e.length !== 0) for (let t of o.getDescendantsOfKind(S.CallExpression).reverse()) {
|
|
54
|
+
if (!w(t, e)) continue;
|
|
55
55
|
let n = t.getArguments()[0];
|
|
56
|
-
if (!(!n || !
|
|
57
|
-
if (!
|
|
56
|
+
if (!(!n || !x.isObjectLiteralExpression(n))) for (let e of n.getProperties()) {
|
|
57
|
+
if (!x.isPropertyAssignment(e) && !x.isShorthandPropertyAssignment(e)) continue;
|
|
58
58
|
let t = e.getNameNode();
|
|
59
|
-
t.getText() === r && (
|
|
59
|
+
t.getText() === r && (x.isShorthandPropertyAssignment(e) ? e.replaceWithText(`${i}: ${r}`) : t.replaceWithText(i), a++);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
return a;
|
|
64
|
-
},
|
|
64
|
+
}, ue = (e, { module: t, name: n }) => {
|
|
65
65
|
let r = 0;
|
|
66
66
|
for (let i of e.getSourceFiles()) {
|
|
67
67
|
let e = i.getImportDeclaration((e) => e.getModuleSpecifierValue() === t);
|
|
68
68
|
e && (e.getNamedImports().some((e) => e.getName() === n) || (e.addNamedImport(n), r++));
|
|
69
69
|
}
|
|
70
70
|
return r;
|
|
71
|
-
},
|
|
71
|
+
}, de = (e, t, n) => {
|
|
72
72
|
let r = e.getImportDeclaration((e) => e.getModuleSpecifierValue() === t);
|
|
73
73
|
r ? r.getNamedImports().some((e) => e.getName() === n) || r.addNamedImport(n) : e.addImportDeclaration({
|
|
74
74
|
moduleSpecifier: t,
|
|
75
75
|
namedImports: [n]
|
|
76
76
|
});
|
|
77
|
-
},
|
|
78
|
-
renameImport:
|
|
79
|
-
renameModuleSpecifier:
|
|
80
|
-
renameJsxProp:
|
|
81
|
-
renameObjectKey:
|
|
82
|
-
addImport:
|
|
77
|
+
}, fe = {
|
|
78
|
+
renameImport: oe,
|
|
79
|
+
renameModuleSpecifier: se,
|
|
80
|
+
renameJsxProp: ce,
|
|
81
|
+
renameObjectKey: le,
|
|
82
|
+
addImport: ue,
|
|
83
83
|
removeImport: (e, { module: t, name: n }) => {
|
|
84
84
|
let r = 0;
|
|
85
85
|
for (let i of e.getSourceFiles()) for (let e of i.getImportDeclarations()) {
|
|
@@ -92,9 +92,9 @@ var x = (e, t, n) => {
|
|
|
92
92
|
changeCallArgs: (e, { module: t, callee: n, reorder: r, drop: i, toObject: a }) => {
|
|
93
93
|
let o = 0;
|
|
94
94
|
for (let s of e.getSourceFiles()) {
|
|
95
|
-
let e =
|
|
96
|
-
if (e.length !== 0) for (let t of s.getDescendantsOfKind(
|
|
97
|
-
if (!
|
|
95
|
+
let e = C(s, t, n);
|
|
96
|
+
if (e.length !== 0) for (let t of s.getDescendantsOfKind(S.CallExpression).reverse()) {
|
|
97
|
+
if (!w(t, e)) continue;
|
|
98
98
|
let n = t.getArguments().map((e) => e.getText());
|
|
99
99
|
if (r && (n = r.map((e) => n[e]).filter((e) => e !== void 0)), i) {
|
|
100
100
|
let e = new Set(i);
|
|
@@ -113,48 +113,49 @@ var x = (e, t, n) => {
|
|
|
113
113
|
wrapCall: (e, { module: t, callee: n, wrapper: r, wrapperModule: i }) => {
|
|
114
114
|
let a = 0;
|
|
115
115
|
for (let o of e.getSourceFiles()) {
|
|
116
|
-
let e =
|
|
116
|
+
let e = C(o, t, n);
|
|
117
117
|
if (e.length === 0) continue;
|
|
118
118
|
let s = !1;
|
|
119
|
-
for (let t of o.getDescendantsOfKind(
|
|
120
|
-
s &&
|
|
119
|
+
for (let t of o.getDescendantsOfKind(S.CallExpression).reverse()) w(t, e) && (t.replaceWithText(`${r}(${t.getText()})`), s = !0, a++);
|
|
120
|
+
s && de(o, i, r);
|
|
121
121
|
}
|
|
122
122
|
return a;
|
|
123
123
|
},
|
|
124
124
|
annotate: (e, { module: t, symbol: n, comment: r }) => {
|
|
125
125
|
let i = 0;
|
|
126
126
|
for (let a of e.getSourceFiles()) {
|
|
127
|
-
let e =
|
|
127
|
+
let e = C(a, t, n);
|
|
128
128
|
if (e.length === 0) continue;
|
|
129
129
|
let o = /* @__PURE__ */ new Map();
|
|
130
|
-
for (let t of a.getDescendantsOfKind(
|
|
130
|
+
for (let t of a.getDescendantsOfKind(S.Identifier)) {
|
|
131
131
|
if (!e.includes(t.getText())) continue;
|
|
132
|
-
let n = t.getFirstAncestorByKind(
|
|
132
|
+
let n = t.getFirstAncestorByKind(S.ExpressionStatement) ?? t.getFirstAncestorByKind(S.VariableStatement) ?? t.getFirstAncestorByKind(S.ReturnStatement);
|
|
133
133
|
n && o.set(n.getStart(), n);
|
|
134
134
|
}
|
|
135
135
|
for (let e of [...o.values()].sort((e, t) => t.getStart() - e.getStart())) e.replaceWithText(`// TODO(voltro-migration): ${r}\n${e.getText()}`), i++;
|
|
136
136
|
}
|
|
137
137
|
return i;
|
|
138
138
|
}
|
|
139
|
-
},
|
|
140
|
-
if (!
|
|
139
|
+
}, pe = /^\d+\.\d+\.\d+$/, T = (e) => {
|
|
140
|
+
if (!pe.test(e.version)) throw Error(`codemod "${e.id}": version must be x.y.z (got "${e.version}")`);
|
|
141
141
|
if (!e.id.startsWith(`${e.version}/`)) throw Error(`codemod "${e.id}": id must start with "${e.version}/" (the version it migrates to)`);
|
|
142
142
|
if (e.kind === "transform" && typeof e.transform != "function") throw Error(`codemod "${e.id}": kind:'transform' requires a transform() function`);
|
|
143
143
|
if (e.kind === "manual" && (e.note === void 0 || e.note.trim() === "")) throw Error(`codemod "${e.id}": kind:'manual' requires a non-empty note`);
|
|
144
|
+
if (e.reach === "beyond-source" && e.kind !== "manual") throw Error(`codemod "${e.id}": reach:'beyond-source' is only meaningful for kind:'manual' — a transform cannot rewrite a file it cannot parse, so it has nothing to say about one.`);
|
|
144
145
|
let t = /^(\d+)/.exec(e.id.slice(e.version.length + 1)), n = t ? Number(t[1]) : 0;
|
|
145
146
|
return {
|
|
146
147
|
...e,
|
|
147
148
|
__codemod: !0,
|
|
148
149
|
seq: n
|
|
149
150
|
};
|
|
150
|
-
},
|
|
151
|
+
}, E = (e) => RegExp(`\\b${e}\\s*(?:<[^()]*>)?\\s*\\(`), me = (e, t) => {
|
|
151
152
|
let n = e.version.split(".").map(Number), r = t.version.split(".").map(Number);
|
|
152
153
|
for (let e = 0; e < 3; e++) {
|
|
153
154
|
let t = (n[e] ?? 0) - (r[e] ?? 0);
|
|
154
155
|
if (t !== 0) return t;
|
|
155
156
|
}
|
|
156
157
|
return e.seq === t.seq ? e.id < t.id ? -1 : +(e.id > t.id) : e.seq - t.seq;
|
|
157
|
-
},
|
|
158
|
+
}, he = T({
|
|
158
159
|
version: "0.3.0",
|
|
159
160
|
id: "0.3.0/01_prod-serve-requires-build",
|
|
160
161
|
title: "Production `voltro serve` requires a precompiled bundle (build before serve)",
|
|
@@ -174,7 +175,7 @@ var x = (e, t, n) => {
|
|
|
174
175
|
"",
|
|
175
176
|
"`voltro dev` and a non-production local `voltro serve` are unchanged (still tsx)."
|
|
176
177
|
].join("\n")
|
|
177
|
-
}),
|
|
178
|
+
}), ge = T({
|
|
178
179
|
version: "0.4.0",
|
|
179
180
|
id: "0.4.0/01_rbac-forbidden-to-scopeerror",
|
|
180
181
|
title: "@voltro/plugin-rbac: `Forbidden` → protocol `ScopeError` (one denial tag)",
|
|
@@ -200,36 +201,36 @@ var x = (e, t, n) => {
|
|
|
200
201
|
let a = r + i;
|
|
201
202
|
a > 0 && n(`rbac Forbidden → ScopeError: rewrote ${a} import site(s). Check any \`new Forbidden({ reason })\` (→ \`new ScopeError({ message })\`) and \`catchTag('Forbidden')\` / \`_tag === 'Forbidden'\` (→ 'ScopeError') by hand.`);
|
|
202
203
|
}
|
|
203
|
-
}),
|
|
204
|
+
}), D = "@voltro/plugin-rbac/web", _e = "@voltro/client", ve = T({
|
|
204
205
|
version: "0.4.0",
|
|
205
206
|
id: "0.4.0/02_rbac-web-to-client-permissions",
|
|
206
207
|
title: "@voltro/plugin-rbac/web removed — the UI gate lives in @voltro/client",
|
|
207
208
|
kind: "transform",
|
|
208
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() ===
|
|
209
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === D)),
|
|
209
210
|
transform: ({ project: e, helpers: t, log: n }) => {
|
|
210
211
|
let r = t.annotate(e, {
|
|
211
|
-
module:
|
|
212
|
+
module: D,
|
|
212
213
|
symbol: "RbacScopeContext",
|
|
213
214
|
comment: "voltro 0.4.0: RbacScopeContext is gone. Read scopes with `usePermissions()` from '@voltro/client' — it returns { scopes }, not the bare array."
|
|
214
215
|
}), i = t.renameImport(e, {
|
|
215
|
-
module:
|
|
216
|
+
module: D,
|
|
216
217
|
from: "RbacScopeProvider",
|
|
217
218
|
to: "PermissionProvider"
|
|
218
219
|
}) + t.renameImport(e, {
|
|
219
|
-
module:
|
|
220
|
+
module: D,
|
|
220
221
|
from: "canFromScopes",
|
|
221
222
|
to: "canCall"
|
|
222
223
|
}) + t.renameImport(e, {
|
|
223
|
-
module:
|
|
224
|
+
module: D,
|
|
224
225
|
from: "canAnyFromScopes",
|
|
225
226
|
to: "canCallAny"
|
|
226
227
|
}), a = t.renameModuleSpecifier(e, {
|
|
227
|
-
from:
|
|
228
|
-
to:
|
|
228
|
+
from: D,
|
|
229
|
+
to: _e
|
|
229
230
|
});
|
|
230
231
|
a > 0 && n(`@voltro/plugin-rbac/web → @voltro/client: repointed ${a} import(s)` + (i > 0 ? `, renamed ${i} symbol(s)` : "") + ". Mount <PermissionProvider scopes={…}> where <RbacScopeProvider> was — same prop, and it now feeds useCan across the whole app." + (r > 0 ? ` ${r} RbacScopeContext site(s) were annotated: switch them to usePermissions().` : ""));
|
|
231
232
|
}
|
|
232
|
-
}),
|
|
233
|
+
}), ye = T({
|
|
233
234
|
version: "0.5.0",
|
|
234
235
|
id: "0.5.0/01_one-terminal-exactly-one",
|
|
235
236
|
title: ".one() now fails on MORE than one row; NoRowFound is a declarable typed error",
|
|
@@ -262,7 +263,7 @@ var x = (e, t, n) => {
|
|
|
262
263
|
" new OptimisticLockError({ table: 'notes', expected: 3 })",
|
|
263
264
|
" • `instanceof` and `err._tag === 'NoRowFound'` checks are unaffected."
|
|
264
265
|
].join("\n")
|
|
265
|
-
}),
|
|
266
|
+
}), be = T({
|
|
266
267
|
version: "0.5.0",
|
|
267
268
|
id: "0.5.0/02_typed-store-rows",
|
|
268
269
|
title: "store.query() now returns the row type — casts on row fields may stop compiling",
|
|
@@ -301,14 +302,14 @@ var x = (e, t, n) => {
|
|
|
301
302
|
"A hand-built descriptor still resolves to `Row`, so untyped call sites are",
|
|
302
303
|
"unaffected."
|
|
303
304
|
].join("\n")
|
|
304
|
-
}),
|
|
305
|
+
}), xe = /VOLTRO_DEV_SESSION_SECRET|voltro-dev-session-secret/, Se = /VOLTRO_SESSION_SECRET\s*(?:\?\?|\|\|)\s*['"`]/, Ce = T({
|
|
305
306
|
version: "0.6.0",
|
|
306
307
|
id: "0.6.0/01_no-dev-session-secret",
|
|
307
308
|
title: "The built-in dev session secret is removed; VOLTRO_SESSION_SECRET is required",
|
|
308
309
|
kind: "manual",
|
|
309
310
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
310
311
|
let t = e.getFullText();
|
|
311
|
-
return
|
|
312
|
+
return xe.test(t) || Se.test(t);
|
|
312
313
|
}),
|
|
313
314
|
note: [
|
|
314
315
|
"The framework no longer has a built-in fallback session secret.",
|
|
@@ -352,14 +353,14 @@ var x = (e, t, n) => {
|
|
|
352
353
|
" voltro secret generate session",
|
|
353
354
|
" `voltro dev` minting one for you locally does NOT cover your deployment."
|
|
354
355
|
].join("\n")
|
|
355
|
-
}),
|
|
356
|
+
}), we = /computeProration|roundHalfAwayFromZero|ProrationInput|ProrationResult|DunningState|DunningCycleResult|DunningSchedule|DEFAULT_DUNNING_SCHEDULE|validateDunningSchedule|retryTimeFor|openDunning|recoverDunning|advanceDunning|runDunningCycle|recordPaymentFailure|recordPaymentSuccess|dunningSchedule|DunningStore|memoryDunningStore|dataStoreDunningStore/, Te = T({
|
|
356
357
|
version: "0.6.0",
|
|
357
358
|
id: "0.6.0/03_billing-on-stripe-sdk",
|
|
358
359
|
title: "Billing moved onto the Stripe SDK; proration + dunning are now Stripe’s",
|
|
359
360
|
kind: "manual",
|
|
360
361
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
361
362
|
let t = e.getFullText();
|
|
362
|
-
return
|
|
363
|
+
return we.test(t) || /@voltro\/plugin-billing/.test(t);
|
|
363
364
|
}),
|
|
364
365
|
note: [
|
|
365
366
|
"`@voltro/plugin-billing` now talks to Stripe through the official SDK, and two",
|
|
@@ -415,7 +416,7 @@ var x = (e, t, n) => {
|
|
|
415
416
|
" Set a `priceId` on every paid plan. A subscription created in the Stripe",
|
|
416
417
|
" Dashboard is now recognised via its price id, which requires the mapping."
|
|
417
418
|
].join("\n")
|
|
418
|
-
}),
|
|
419
|
+
}), Ee = T({
|
|
419
420
|
version: "0.6.0",
|
|
420
421
|
id: "0.6.0/02_subscription-state-union",
|
|
421
422
|
title: "SubscriptionState is a discriminated union — `loading` now narrows `data`",
|
|
@@ -462,7 +463,7 @@ var x = (e, t, n) => {
|
|
|
462
463
|
"`error` set. If you branch only on `loading`, check `error` too, or that",
|
|
463
464
|
"case renders a skeleton forever."
|
|
464
465
|
].join("\n")
|
|
465
|
-
}),
|
|
466
|
+
}), De = T({
|
|
466
467
|
version: "0.6.0",
|
|
467
468
|
id: "0.6.0/04_relation-key-validation",
|
|
468
469
|
title: "relation key options are validated at boot; `one` now fails on more than one match",
|
|
@@ -503,28 +504,28 @@ var x = (e, t, n) => {
|
|
|
503
504
|
"read it now rather than waiting: `sourceKey` is almost always what a",
|
|
504
505
|
"\"parent\" relation means."
|
|
505
506
|
].join("\n")
|
|
506
|
-
}),
|
|
507
|
+
}), Oe = "@voltro/plugin-atlassian", ke = "atlassianPlugin", Ae = (e) => {
|
|
507
508
|
let t = [];
|
|
508
|
-
for (let n of e.getImportDeclarations()) if (n.getModuleSpecifierValue() ===
|
|
509
|
+
for (let n of e.getImportDeclarations()) if (n.getModuleSpecifierValue() === Oe) for (let e of n.getNamedImports()) e.getName() === ke && t.push(e.getAliasNode()?.getText() ?? e.getName());
|
|
509
510
|
return t;
|
|
510
|
-
}, je =
|
|
511
|
+
}, je = T({
|
|
511
512
|
version: "0.6.0",
|
|
512
513
|
id: "0.6.0/05_atlassian-avatar-mode",
|
|
513
514
|
title: "atlassianPlugin's `avatar` config requires an explicit `mode: 'service' | 'perUser'`",
|
|
514
515
|
kind: "transform",
|
|
515
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
516
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ae(e).length > 0),
|
|
516
517
|
transform: ({ project: e, log: t }) => {
|
|
517
518
|
let n = 0;
|
|
518
519
|
for (let t of e.getSourceFiles()) {
|
|
519
|
-
let e =
|
|
520
|
-
if (e.length !== 0) for (let r of t.getDescendantsOfKind(
|
|
520
|
+
let e = Ae(t);
|
|
521
|
+
if (e.length !== 0) for (let r of t.getDescendantsOfKind(S.CallExpression)) {
|
|
521
522
|
if (!e.includes(r.getExpression().getText())) continue;
|
|
522
523
|
let t = r.getArguments()[0];
|
|
523
|
-
if (!t || !
|
|
524
|
+
if (!t || !x.isObjectLiteralExpression(t)) continue;
|
|
524
525
|
let i = t.getProperty("avatar");
|
|
525
|
-
if (!i || !
|
|
526
|
+
if (!i || !x.isPropertyAssignment(i)) continue;
|
|
526
527
|
let a = i.getInitializer();
|
|
527
|
-
!a || !
|
|
528
|
+
!a || !x.isObjectLiteralExpression(a) || a.getProperty("mode") || (a.insertPropertyAssignment(0, {
|
|
528
529
|
name: "mode",
|
|
529
530
|
initializer: "'service'"
|
|
530
531
|
}), n++);
|
|
@@ -532,7 +533,7 @@ var x = (e, t, n) => {
|
|
|
532
533
|
}
|
|
533
534
|
n > 0 && (t(`atlassian avatar: added mode:'service' to ${n} config${n === 1 ? "" : "s"}`), t(" → to use the viewing user's own PAT instead, switch to mode:'perUser' and add resolveSubject"));
|
|
534
535
|
}
|
|
535
|
-
}), Me =
|
|
536
|
+
}), Me = T({
|
|
536
537
|
version: "0.6.0",
|
|
537
538
|
id: "0.6.0/06_ui-strings-connect-account",
|
|
538
539
|
title: "UiStrings gained a required `connectAccount` section",
|
|
@@ -576,7 +577,7 @@ var x = (e, t, n) => {
|
|
|
576
577
|
"Alternatively, switch the annotation to `PartialUiStrings` if the object",
|
|
577
578
|
"was only ever an override bag."
|
|
578
579
|
].join("\n")
|
|
579
|
-
}), Ne =
|
|
580
|
+
}), Ne = E("setRowFilter"), Pe = /\bresolveRowFilterScope\b/, Fe = T({
|
|
580
581
|
version: "0.7.0",
|
|
581
582
|
id: "0.7.0/01_row-filter-load-failure",
|
|
582
583
|
title: "A row filter that fails to load now errors instead of returning zero rows",
|
|
@@ -654,14 +655,14 @@ var x = (e, t, n) => {
|
|
|
654
655
|
" \"every row you could see was just deleted\". Make sure your subscription",
|
|
655
656
|
" error handling surfaces this rather than swallowing it."
|
|
656
657
|
].join("\n")
|
|
657
|
-
}), Ie =
|
|
658
|
+
}), Ie = T({
|
|
658
659
|
version: "0.7.0",
|
|
659
660
|
id: "0.7.0/02_testing-invoke-effect-handlers",
|
|
660
661
|
title: "@voltro/testing: invoke() runs Effect handlers; executor types widened",
|
|
661
662
|
kind: "manual",
|
|
662
663
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
663
664
|
let t = e.getFullText();
|
|
664
|
-
return t.includes("@voltro/testing") ?
|
|
665
|
+
return t.includes("@voltro/testing") ? E("invoke").test(t) || /\bProcedureExecutor\b/.test(t) : !1;
|
|
665
666
|
}),
|
|
666
667
|
note: [
|
|
667
668
|
"`invoke(descriptor, executor, input, ctx)` now DETECTS an Effect-returning",
|
|
@@ -709,7 +710,7 @@ var x = (e, t, n) => {
|
|
|
709
710
|
"transaction, the deadlock replay, `afterCommit` and the plugin interceptor",
|
|
710
711
|
"chain wrap an Effect handler exactly as they wrap an async one."
|
|
711
712
|
].join("\n")
|
|
712
|
-
}), Le = /\bpasswordHash\b/, Re = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, ze =
|
|
713
|
+
}), Le = /\bpasswordHash\b/, Re = /\b(UserStore|UserRecord|memoryUserStore|postgresUserStore)\b/, ze = T({
|
|
713
714
|
version: "0.8.0",
|
|
714
715
|
id: "0.8.0/01_optional-password-hash",
|
|
715
716
|
title: "`UserRecord.passwordHash` is optional — password-less identities are now first-class",
|
|
@@ -787,7 +788,7 @@ var x = (e, t, n) => {
|
|
|
787
788
|
" to match and make sure your read path maps a NULL column to `null`/",
|
|
788
789
|
" `undefined` rather than to `''`-by-coercion."
|
|
789
790
|
].join("\n")
|
|
790
|
-
}), Be =
|
|
791
|
+
}), Be = T({
|
|
791
792
|
version: "0.8.0",
|
|
792
793
|
id: "0.8.0/02_ssr-streams-and-loaderdata",
|
|
793
794
|
title: "`ssr` pages stream; `useLoaderData<T>()` returns `LoaderData<T>`",
|
|
@@ -839,7 +840,7 @@ var x = (e, t, n) => {
|
|
|
839
840
|
"docs. `defer()` requires `renderMode: 'ssr'` and `interactive: 'full'`;",
|
|
840
841
|
"every other combination is a hard error naming the page."
|
|
841
842
|
].join("\n")
|
|
842
|
-
}), Ve = /renderMode\s*[=:]\s*['"](spa|client)['"]/, He = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, Ue =
|
|
843
|
+
}), Ve = /renderMode\s*[=:]\s*['"](spa|client)['"]/, He = /(^|[/\\])pages[/\\].*[/\\]?layout\.tsx$/, Ue = T({
|
|
843
844
|
version: "0.9.0",
|
|
844
845
|
id: "0.9.0/01_ssr-layout-shell-for-spa",
|
|
845
846
|
title: "A `renderMode:'spa'` page under a layout now server-renders that layout",
|
|
@@ -884,35 +885,35 @@ var x = (e, t, n) => {
|
|
|
884
885
|
"Nothing changes for `static` / `ssr` / `isr` pages, or for a spa page that",
|
|
885
886
|
"has no layout (still a pure client mount)."
|
|
886
887
|
].join("\n")
|
|
887
|
-
}),
|
|
888
|
+
}), O = "client", k = "spa", A = (e) => {
|
|
888
889
|
if (!e) return null;
|
|
889
|
-
let t =
|
|
890
|
-
return
|
|
891
|
-
},
|
|
890
|
+
let t = x.isAsExpression(e) ? e.getExpression() : e;
|
|
891
|
+
return x.isStringLiteral(t) ? t : null;
|
|
892
|
+
}, j = (e) => {
|
|
892
893
|
let t = [];
|
|
893
|
-
for (let n of e.getDescendantsOfKind(
|
|
894
|
+
for (let n of e.getDescendantsOfKind(S.VariableDeclaration)) {
|
|
894
895
|
if (n.getName() !== "renderMode") continue;
|
|
895
|
-
let e =
|
|
896
|
+
let e = A(n.getInitializer());
|
|
896
897
|
e && t.push(e);
|
|
897
898
|
}
|
|
898
|
-
for (let n of e.getDescendantsOfKind(
|
|
899
|
+
for (let n of e.getDescendantsOfKind(S.PropertyAssignment)) {
|
|
899
900
|
if (n.getName() !== "renderMode") continue;
|
|
900
|
-
let e =
|
|
901
|
+
let e = A(n.getInitializer());
|
|
901
902
|
e && t.push(e);
|
|
902
903
|
}
|
|
903
904
|
return t;
|
|
904
|
-
},
|
|
905
|
+
}, We = T({
|
|
905
906
|
version: "0.10.0",
|
|
906
907
|
id: "0.10.0/01_render-mode-client-to-spa",
|
|
907
908
|
title: "renderMode is a closed set — `'client'` becomes `'spa'`",
|
|
908
909
|
kind: "transform",
|
|
909
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
910
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => j(e).some((e) => e.getLiteralValue() === O)),
|
|
910
911
|
transform: ({ project: e, log: t }) => {
|
|
911
912
|
let n = 0, r = /* @__PURE__ */ new Set();
|
|
912
|
-
for (let t of e.getSourceFiles()) for (let e of
|
|
913
|
-
n > 0 && (t(`renderMode: rewrote ${n} \`'${
|
|
913
|
+
for (let t of e.getSourceFiles()) for (let e of j(t)) e.getLiteralValue() === O && (e.setLiteralValue(k), n++, r.add(t.getFilePath()));
|
|
914
|
+
n > 0 && (t(`renderMode: rewrote ${n} \`'${O}'\` → \`'${k}'\` in ${r.size} file${r.size === 1 ? "" : "s"}`), t(" → 'spa' is the mode those pages already behaved as: no prerender, client-rendered"), t(" → new in 0.9.0: if such a page sits under a layout, that layout now server-renders (see the note above)"));
|
|
914
915
|
}
|
|
915
|
-
}),
|
|
916
|
+
}), Ge = T({
|
|
916
917
|
version: "0.10.0",
|
|
917
918
|
id: "0.10.0/02_router-state-carries-its-path",
|
|
918
919
|
title: "`renderRouterStateScript` now requires the `path` it rendered",
|
|
@@ -949,7 +950,7 @@ var x = (e, t, n) => {
|
|
|
949
950
|
"Trailing slashes, a trailing `/index.html`, and percent-encoding are normalised",
|
|
950
951
|
"before the comparison, so the usual static-host shapes still hydrate."
|
|
951
952
|
].join("\n")
|
|
952
|
-
}),
|
|
953
|
+
}), Ke = T({
|
|
953
954
|
version: "0.10.0",
|
|
954
955
|
id: "0.10.0/03_workflow-caller-context-resolved",
|
|
955
956
|
title: "workflow `buildContext` receives a RESOLVED caller context",
|
|
@@ -995,7 +996,7 @@ var x = (e, t, n) => {
|
|
|
995
996
|
"it receives the resolved context too, so `callerContext?.subject ?? …` there",
|
|
996
997
|
"becomes `callerContext.subject`."
|
|
997
998
|
].join("\n")
|
|
998
|
-
}),
|
|
999
|
+
}), qe = T({
|
|
999
1000
|
version: "0.10.0",
|
|
1000
1001
|
id: "0.10.0/04_seed-run-status-skipped",
|
|
1001
1002
|
title: "seed run status gained `skipped`",
|
|
@@ -1036,7 +1037,7 @@ var x = (e, t, n) => {
|
|
|
1036
1037
|
"`Schema.Literal`, a zod enum, a DB check constraint), widen it there too — an",
|
|
1037
1038
|
"unlisted third state is a DECODE failure, not a rendering oddity."
|
|
1038
1039
|
].join("\n")
|
|
1039
|
-
}),
|
|
1040
|
+
}), Je = T({
|
|
1040
1041
|
version: "0.10.0",
|
|
1041
1042
|
id: "0.10.0/05_subscription-idle-state",
|
|
1042
1043
|
title: "`useSubscription` gained an idle state for `skip`",
|
|
@@ -1088,7 +1089,7 @@ var x = (e, t, n) => {
|
|
|
1088
1089
|
"time's data), hold it in your own state — the subscription no longer claims to",
|
|
1089
1090
|
"have data it was told to stop fetching."
|
|
1090
1091
|
].join("\n")
|
|
1091
|
-
}),
|
|
1092
|
+
}), Ye = T({
|
|
1092
1093
|
version: "0.11.0",
|
|
1093
1094
|
id: "0.11.0/01_step-options-type-params",
|
|
1094
1095
|
title: "`StepOptions` takes the context type first",
|
|
@@ -1116,7 +1117,7 @@ var x = (e, t, n) => {
|
|
|
1116
1117
|
"If the step reads nothing from earlier steps, `StepOptions<Record<never, never>,",
|
|
1117
1118
|
"Result>` is the honest spelling."
|
|
1118
1119
|
].join("\n")
|
|
1119
|
-
}),
|
|
1120
|
+
}), Xe = T({
|
|
1120
1121
|
version: "0.12.0",
|
|
1121
1122
|
id: "0.12.0/01_outbound-http-ssrf-guard",
|
|
1122
1123
|
title: "Outbound HttpClient blocks internal targets by default (add `http.allowHosts` if you call one)",
|
|
@@ -1156,7 +1157,7 @@ var x = (e, t, n) => {
|
|
|
1156
1157
|
"private address (DNS rebinding) still passes. Pair with network-layer egress",
|
|
1157
1158
|
"control if that is in your threat model."
|
|
1158
1159
|
].join("\n")
|
|
1159
|
-
}),
|
|
1160
|
+
}), Ze = T({
|
|
1160
1161
|
version: "0.12.0",
|
|
1161
1162
|
id: "0.12.0/02_api-key-revoke-tenant-scope",
|
|
1162
1163
|
title: "apiKeyService.revoke/rotate now require the caller's tenantId",
|
|
@@ -1185,7 +1186,7 @@ var x = (e, t, n) => {
|
|
|
1185
1186
|
"The framework's own `/v1/api-keys/revoke` route already passes the caller's",
|
|
1186
1187
|
"tenant — no change needed if you only use the shipped routes."
|
|
1187
1188
|
].join("\n")
|
|
1188
|
-
}),
|
|
1189
|
+
}), Qe = T({
|
|
1189
1190
|
version: "0.12.0",
|
|
1190
1191
|
id: "0.12.0/03_api-key-on-behalf-of",
|
|
1191
1192
|
title: "API keys: createdBy is provenance, onBehalfOf is ownership",
|
|
@@ -1219,7 +1220,7 @@ var x = (e, t, n) => {
|
|
|
1219
1220
|
"specific one against what the body asks for. Grant them in your role map. A key",
|
|
1220
1221
|
"can never carry scopes its issuer does not hold."
|
|
1221
1222
|
].join("\n")
|
|
1222
|
-
}),
|
|
1223
|
+
}), $e = T({
|
|
1223
1224
|
version: "0.12.0",
|
|
1224
1225
|
id: "0.12.0/04_inspect-fail-closed",
|
|
1225
1226
|
title: "The inspect surface requires VOLTRO_INSPECT_TOKEN (previously open when unset)",
|
|
@@ -1248,7 +1249,7 @@ var x = (e, t, n) => {
|
|
|
1248
1249
|
"",
|
|
1249
1250
|
"To turn the surface off entirely instead, set `VOLTRO_INSPECT=off`."
|
|
1250
1251
|
].join("\n")
|
|
1251
|
-
}),
|
|
1252
|
+
}), et = T({
|
|
1252
1253
|
version: "0.13.0",
|
|
1253
1254
|
id: "0.13.0/01_bearer-gates-fail-closed",
|
|
1254
1255
|
title: "checkBearer is fail-closed; scimPlugin refuses an empty token",
|
|
@@ -1285,7 +1286,7 @@ var x = (e, t, n) => {
|
|
|
1285
1286
|
" • `@voltro/plugin-prometheus` already passes `{ openWhenUnset: true }` — its",
|
|
1286
1287
|
" `PROMETHEUS_TOKEN` is documented as optional. Nothing to do there."
|
|
1287
1288
|
].join("\n")
|
|
1288
|
-
}),
|
|
1289
|
+
}), tt = T({
|
|
1289
1290
|
version: "0.13.0",
|
|
1290
1291
|
id: "0.13.0/02_storage-grants-owner-only",
|
|
1291
1292
|
title: "storage.share / revoke / listGrants require ownership",
|
|
@@ -1316,7 +1317,7 @@ var x = (e, t, n) => {
|
|
|
1316
1317
|
" returning the grant or null. `storage.revoke` is handed a grant id, so it has",
|
|
1317
1318
|
" to resolve the object before it can check the owner."
|
|
1318
1319
|
].join("\n")
|
|
1319
|
-
}),
|
|
1320
|
+
}), nt = T({
|
|
1320
1321
|
version: "0.13.0",
|
|
1321
1322
|
id: "0.13.0/03_apply-plan-convergence-proof",
|
|
1322
1323
|
title: "applyPlan requires a `replan` convergence proof",
|
|
@@ -1349,7 +1350,7 @@ var x = (e, t, n) => {
|
|
|
1349
1350
|
" • `AppliedMigration` gained `appliedOps` (operations actually EXECUTED, from",
|
|
1350
1351
|
" the per-op results). If you construct that value in a test fixture, add it."
|
|
1351
1352
|
].join("\n")
|
|
1352
|
-
}),
|
|
1353
|
+
}), rt = T({
|
|
1353
1354
|
version: "0.14.0",
|
|
1354
1355
|
id: "0.14.0/01_resolve-scopes-store",
|
|
1355
1356
|
title: "auth.resolveScopes receives the app DataStore",
|
|
@@ -1387,19 +1388,19 @@ var x = (e, t, n) => {
|
|
|
1387
1388
|
" deliberately does not, because only you know how fast a role change must",
|
|
1388
1389
|
" take effect."
|
|
1389
1390
|
].join("\n")
|
|
1390
|
-
}),
|
|
1391
|
-
if (!
|
|
1391
|
+
}), it = (e) => {
|
|
1392
|
+
if (!x.isCallExpression(e)) return !1;
|
|
1392
1393
|
let t = e.getExpression();
|
|
1393
|
-
return
|
|
1394
|
-
}, at = (e) => e.getDescendantsOfKind(
|
|
1395
|
-
let t = 0, n = e.getDescendantsOfKind(
|
|
1394
|
+
return x.isPropertyAccessExpression(t) && t.getName() === "reactive" && e.getArguments().length === 0;
|
|
1395
|
+
}, at = (e) => e.getDescendantsOfKind(S.CallExpression).some(it), ot = (e) => {
|
|
1396
|
+
let t = 0, n = e.getDescendantsOfKind(S.CallExpression);
|
|
1396
1397
|
for (let e of n) {
|
|
1397
|
-
if (e.wasForgotten() || !
|
|
1398
|
+
if (e.wasForgotten() || !it(e)) continue;
|
|
1398
1399
|
let n = e.getExpression();
|
|
1399
|
-
|
|
1400
|
+
x.isPropertyAccessExpression(n) && (e.replaceWithText(n.getExpression().getText()), t++);
|
|
1400
1401
|
}
|
|
1401
1402
|
return t;
|
|
1402
|
-
}, st =
|
|
1403
|
+
}, st = T({
|
|
1403
1404
|
version: "0.14.0",
|
|
1404
1405
|
id: "0.14.0/02_reactive-by-default",
|
|
1405
1406
|
title: "Reactivity is the default; .reactive() is removed",
|
|
@@ -1426,7 +1427,7 @@ var x = (e, t, n) => {
|
|
|
1426
1427
|
}
|
|
1427
1428
|
return n > 0 && t(`removed ${n} .reactive() call(s) across ${r} file(s) — reactivity is the default`), n > 0;
|
|
1428
1429
|
}
|
|
1429
|
-
}), ct =
|
|
1430
|
+
}), ct = T({
|
|
1430
1431
|
version: "0.14.0",
|
|
1431
1432
|
id: "0.14.0/07_dev-remote-file-migrations",
|
|
1432
1433
|
title: "voltro dev refuses unattended file migrations against a remote database",
|
|
@@ -1469,14 +1470,14 @@ var x = (e, t, n) => {
|
|
|
1469
1470
|
"Unchanged: `voltro db files`, `voltro db apply` and `voltro serve`. This gates the",
|
|
1470
1471
|
"AUTOMATIC boot path only; an explicit command is already an explicit decision."
|
|
1471
1472
|
].join("\n")
|
|
1472
|
-
}), lt = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()],
|
|
1473
|
-
let r =
|
|
1473
|
+
}), lt = (e) => [...e.getImportDeclarations(), ...e.getExportDeclarations()], M = (e) => g(e).replace(/\.tsx?$/, ""), ut = (e, t, n) => {
|
|
1474
|
+
let r = M(t), i = M(n);
|
|
1474
1475
|
if (r === i) return;
|
|
1475
1476
|
let a = /\.tsx?$/.exec(e)?.[0] ?? "", o = a === "" ? e : e.slice(0, -a.length);
|
|
1476
1477
|
if (!o.endsWith(`/${r}`) && o !== r) return;
|
|
1477
1478
|
let s = a === "" ? "" : /\.tsx$/.test(n) ? ".tsx" : ".ts";
|
|
1478
1479
|
return `${o.slice(0, o.length - r.length)}${i}${s}`;
|
|
1479
|
-
},
|
|
1480
|
+
}, N = (e, t) => {
|
|
1480
1481
|
let n = e.getFilePath(), r = [];
|
|
1481
1482
|
for (let i of e.getProject().getSourceFiles()) for (let e of lt(i)) {
|
|
1482
1483
|
let i = e.getModuleSpecifierValue();
|
|
@@ -1493,16 +1494,16 @@ var x = (e, t, n) => {
|
|
|
1493
1494
|
}, dt = (e) => {
|
|
1494
1495
|
let t = e.getProject().getCompilerOptions().paths;
|
|
1495
1496
|
if (t === void 0) return;
|
|
1496
|
-
let n =
|
|
1497
|
+
let n = M(e.getFilePath()), r = e.getFilePath().slice(0, e.getFilePath().lastIndexOf("/"));
|
|
1497
1498
|
for (let [e, i] of Object.entries(t)) if (!e.endsWith("*")) for (let t of i ?? []) {
|
|
1498
1499
|
if (!/\.tsx?$/.test(t)) continue;
|
|
1499
|
-
let i =
|
|
1500
|
+
let i = M(t), a = t.slice(0, t.lastIndexOf("/"));
|
|
1500
1501
|
if (i === n && r.endsWith(a.replace(/^\.\//, ""))) return e;
|
|
1501
1502
|
}
|
|
1502
|
-
}, ft = /\.page\.tsx$/,
|
|
1503
|
+
}, ft = /\.page\.tsx$/, P = (e) => {
|
|
1503
1504
|
let t = e.includes("\\src\\pages\\") ? "\\src\\pages\\" : "/src/pages/", n = e.indexOf(t);
|
|
1504
1505
|
return n === -1 ? null : e.slice(0, n);
|
|
1505
|
-
}, pt = (e) => e.endsWith(".tsx") && !o.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !ft.test(e), mt =
|
|
1506
|
+
}, pt = (e) => e.endsWith(".tsx") && !o.has(e) && !d(e) && !/\.island\.tsx$/.test(e) && !ft.test(e), mt = T({
|
|
1506
1507
|
version: "0.14.0",
|
|
1507
1508
|
id: "0.14.0/03_pages-suffix",
|
|
1508
1509
|
title: "Pages are declared with a .page.tsx suffix",
|
|
@@ -1517,11 +1518,11 @@ var x = (e, t, n) => {
|
|
|
1517
1518
|
transform: ({ project: e, log: t }) => {
|
|
1518
1519
|
let n = /* @__PURE__ */ new Map(), r = e.getSourceFiles().filter((t) => {
|
|
1519
1520
|
let r = t.getFilePath();
|
|
1520
|
-
if (!pt(
|
|
1521
|
-
let i =
|
|
1521
|
+
if (!pt(g(r))) return !1;
|
|
1522
|
+
let i = P(r);
|
|
1522
1523
|
if (i === null) return !1;
|
|
1523
1524
|
let a = n.get(i);
|
|
1524
|
-
return a === void 0 && (a = e.getSourceFile(
|
|
1525
|
+
return a === void 0 && (a = e.getSourceFile(v(i, "app.config.ts")) !== void 0 || e.getSourceFile(v(i, "app.config.tsx")) !== void 0, n.set(i, a)), a;
|
|
1525
1526
|
});
|
|
1526
1527
|
if (r.length === 0) return;
|
|
1527
1528
|
let i = /* @__PURE__ */ new Map();
|
|
@@ -1533,16 +1534,16 @@ var x = (e, t, n) => {
|
|
|
1533
1534
|
}
|
|
1534
1535
|
let a = [], o = 0;
|
|
1535
1536
|
for (let t of r) {
|
|
1536
|
-
let n = t.getFilePath(), r =
|
|
1537
|
+
let n = t.getFilePath(), r = g(n), s = _(n), c = r.slice(0, -4);
|
|
1537
1538
|
for (let t of [".test.tsx", ".spec.tsx"]) {
|
|
1538
|
-
let n =
|
|
1539
|
-
r !== void 0 && (
|
|
1539
|
+
let n = v(s, `${c}${t}`), r = e.getSourceFile(n);
|
|
1540
|
+
r !== void 0 && (N(r, v(s, `${c}.page${t}`)), o += 1);
|
|
1540
1541
|
}
|
|
1541
|
-
|
|
1542
|
+
N(t, v(s, `${c}.page.tsx`));
|
|
1542
1543
|
let l = i.get(n);
|
|
1543
|
-
if (l !== void 0 && l.length > 0 && !l.every((e) => d(
|
|
1544
|
-
let e =
|
|
1545
|
-
a.push(`${
|
|
1544
|
+
if (l !== void 0 && l.length > 0 && !l.every((e) => d(g(e)))) {
|
|
1545
|
+
let e = P(n) ?? _(n), t = [...new Set(l)].map((t) => ee(e, t));
|
|
1546
|
+
a.push(`${ee(e, n)} ← imported by ${t.join(", ")}`);
|
|
1546
1547
|
}
|
|
1547
1548
|
}
|
|
1548
1549
|
if (t(`renamed ${r.length} route file(s) to *.page.tsx${o > 0 ? ` (+${o} co-located test file(s))` : ""}`), a.length > 0) {
|
|
@@ -1562,23 +1563,23 @@ var x = (e, t, n) => {
|
|
|
1562
1563
|
e,
|
|
1563
1564
|
r
|
|
1564
1565
|
], gt = (e) => {
|
|
1565
|
-
let n =
|
|
1566
|
+
let n = g(e);
|
|
1566
1567
|
return !/\.tsx?$/.test(n) || o.has(n) || t(n) || n === "index.ts" || n === "index.tsx" || n === "app.config.ts" ? !1 : !ht.some((e) => e.test(n));
|
|
1567
1568
|
}, _t = (e) => {
|
|
1568
1569
|
let t = [], n = [], r = [], i = [];
|
|
1569
1570
|
for (let [a, o] of e.getExportedDeclarations()) {
|
|
1570
1571
|
let e = o[0];
|
|
1571
1572
|
if (e === void 0) continue;
|
|
1572
|
-
if (
|
|
1573
|
+
if (x.isInterfaceDeclaration(e) || x.isTypeAliasDeclaration(e)) {
|
|
1573
1574
|
i.push(a);
|
|
1574
1575
|
continue;
|
|
1575
1576
|
}
|
|
1576
1577
|
if (a === "default") {
|
|
1577
|
-
if (!(
|
|
1578
|
+
if (!(x.isFunctionDeclaration(e) || x.isClassDeclaration(e) || x.isArrowFunction(e) || x.isFunctionExpression(e))) {
|
|
1578
1579
|
r.push("default");
|
|
1579
1580
|
continue;
|
|
1580
1581
|
}
|
|
1581
|
-
let n =
|
|
1582
|
+
let n = x.isFunctionDeclaration(e) || x.isClassDeclaration(e) ? e.getName() : void 0;
|
|
1582
1583
|
t.push(n ?? "Default");
|
|
1583
1584
|
continue;
|
|
1584
1585
|
}
|
|
@@ -1593,8 +1594,8 @@ var x = (e, t, n) => {
|
|
|
1593
1594
|
};
|
|
1594
1595
|
}, vt = (e, t) => {
|
|
1595
1596
|
let { components: n, hooks: r, others: i, types: a } = e;
|
|
1596
|
-
return n.length + r.length + i.length === 0 ? a.length > 0 ? ".types" : null : t &&
|
|
1597
|
-
}, yt =
|
|
1597
|
+
return n.length + r.length + i.length === 0 ? a.length > 0 ? ".types" : null : t && h(n) && r.length === 0 && i.length === 0 ? ".component.tsx" : n.length === 0 && r.length === 1 && i.length === 0 ? ".hook" : null;
|
|
1598
|
+
}, yt = T({
|
|
1598
1599
|
version: "0.14.0",
|
|
1599
1600
|
id: "0.14.0/04_file-taxonomy",
|
|
1600
1601
|
title: "Web files declare their contract (*.component.tsx / *.hook.ts / *.types.ts)",
|
|
@@ -1603,23 +1604,23 @@ var x = (e, t, n) => {
|
|
|
1603
1604
|
let n = [], r = [], i = 0, a = [], o = e.getSourceFiles().map((e) => e.getFilePath()), s = [...new Set(o.map((e) => {
|
|
1604
1605
|
let t = e.indexOf("/src/");
|
|
1605
1606
|
return t === -1 ? null : e.slice(0, t);
|
|
1606
|
-
}).filter((e) => e !== null))].flatMap((e) =>
|
|
1607
|
+
}).filter((e) => e !== null))].flatMap((e) => m(e, o));
|
|
1607
1608
|
if (s.length > 0) for (let e of s) t(` exempt (not ours): ${e.dir} — ${e.source}`);
|
|
1608
1609
|
for (let t of e.getSourceFiles()) {
|
|
1609
1610
|
let e = t.getFilePath();
|
|
1610
1611
|
if (!gt(e) || p(e, s)) continue;
|
|
1611
1612
|
let o = dt(t);
|
|
1612
1613
|
if (o !== void 0) {
|
|
1613
|
-
a.push(`${
|
|
1614
|
+
a.push(`${g(e)} (tsconfig paths: "${o}")`);
|
|
1614
1615
|
continue;
|
|
1615
1616
|
}
|
|
1616
1617
|
let c = e.endsWith(".tsx"), l = _t(t), u = vt(l, c);
|
|
1617
1618
|
if (u === null) {
|
|
1618
|
-
l.components.length > 0 && l.hooks.length > 0 && r.push(`${
|
|
1619
|
+
l.components.length > 0 && l.hooks.length > 0 && r.push(`${g(e)} (component ${l.components.join("/")} + hook ${l.hooks.join("/")})`);
|
|
1619
1620
|
continue;
|
|
1620
1621
|
}
|
|
1621
|
-
let d =
|
|
1622
|
-
i +=
|
|
1622
|
+
let d = g(e).replace(/\.tsx?$/, ""), f = u === ".component.tsx" ? ".component.tsx" : `${u}${c ? ".tsx" : ".ts"}`;
|
|
1623
|
+
i += N(t, v(_(e), `${d}${f}`)), n.push(`${d}${f}`);
|
|
1623
1624
|
}
|
|
1624
1625
|
if (n.length > 0 && (t(`declared ${n.length} file(s): component / hook / types`), i > 0 && t(` rewrote ${i} aliased import specifier(s) (@/… and friends)`)), a.length > 0) {
|
|
1625
1626
|
t(""), t(`left alone — a tsconfig \`paths\` entry names ${a.length} of these files directly:`);
|
|
@@ -1632,7 +1633,7 @@ var x = (e, t, n) => {
|
|
|
1632
1633
|
r.length > 20 && t(` (+${r.length - 20} more)`);
|
|
1633
1634
|
}
|
|
1634
1635
|
}
|
|
1635
|
-
}), bt =
|
|
1636
|
+
}), bt = T({
|
|
1636
1637
|
version: "0.14.0",
|
|
1637
1638
|
id: "0.14.0/05_typed-tracking",
|
|
1638
1639
|
title: "Tracking catalogues are typed against the component props",
|
|
@@ -1669,7 +1670,7 @@ var x = (e, t, n) => {
|
|
|
1669
1670
|
"Read each one before changing it: a cast that no longer compiles is sometimes",
|
|
1670
1671
|
"a real type disagreement the old signature was hiding."
|
|
1671
1672
|
].join("\n")
|
|
1672
|
-
}), xt =
|
|
1673
|
+
}), xt = T({
|
|
1673
1674
|
version: "0.14.0",
|
|
1674
1675
|
id: "0.14.0/06_voltro-test-skips-e2e",
|
|
1675
1676
|
title: "`voltro test` leaves `e2e/` to `voltro e2e`",
|
|
@@ -1697,7 +1698,7 @@ var x = (e, t, n) => {
|
|
|
1697
1698
|
}, Ct = (e) => {
|
|
1698
1699
|
for (let [, t] of e.getExportedDeclarations()) if (t[0] !== void 0) return !1;
|
|
1699
1700
|
return e.getExportDeclarations().length === 0 && e.getExportAssignments().length === 0;
|
|
1700
|
-
}, wt =
|
|
1701
|
+
}, wt = T({
|
|
1701
1702
|
version: "0.15.0",
|
|
1702
1703
|
id: "0.15.0/01_undo-mistaken-taxonomy-renames",
|
|
1703
1704
|
title: "Undo the two file renames 0.14.0 got wrong (framework primitives, side-effect modules)",
|
|
@@ -1714,14 +1715,14 @@ var x = (e, t, n) => {
|
|
|
1714
1715
|
transform: ({ project: e, log: t }) => {
|
|
1715
1716
|
let n = [], r = [];
|
|
1716
1717
|
for (let t of e.getSourceFiles()) {
|
|
1717
|
-
let e = t.getFilePath(), i =
|
|
1718
|
+
let e = t.getFilePath(), i = g(e), a = St(i);
|
|
1718
1719
|
if (a !== null) {
|
|
1719
|
-
|
|
1720
|
+
N(t, v(_(e), a)), n.push(`${i} → ${a}`);
|
|
1720
1721
|
continue;
|
|
1721
1722
|
}
|
|
1722
1723
|
if (/\.types\.tsx?$/.test(i) && Ct(t)) {
|
|
1723
1724
|
let n = i.replace(/\.types(\.tsx?)$/, "$1");
|
|
1724
|
-
|
|
1725
|
+
N(t, v(_(e), n)), r.push(`${i} → ${n}`);
|
|
1725
1726
|
}
|
|
1726
1727
|
}
|
|
1727
1728
|
if (n.length > 0) {
|
|
@@ -1735,53 +1736,53 @@ var x = (e, t, n) => {
|
|
|
1735
1736
|
}
|
|
1736
1737
|
n.length === 0 && r.length === 0 && t("nothing to undo — 0.14.0 renamed neither a framework file nor a side-effect module here.");
|
|
1737
1738
|
}
|
|
1738
|
-
}),
|
|
1739
|
+
}), F = ["@voltro/client", "@voltro/web"], I = "@voltro/protocol", Tt = T({
|
|
1739
1740
|
version: "0.15.0",
|
|
1740
1741
|
id: "0.15.0/02_error-tag-in-protocol",
|
|
1741
1742
|
title: "`errorTag` is imported from @voltro/protocol",
|
|
1742
1743
|
kind: "transform",
|
|
1743
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) =>
|
|
1744
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => F.includes(e.getModuleSpecifierValue()) && e.getNamedImports().some((e) => e.getName() === "errorTag"))),
|
|
1744
1745
|
transform: ({ project: e, log: t }) => {
|
|
1745
1746
|
let n = 0;
|
|
1746
1747
|
for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
|
|
1747
|
-
if (!
|
|
1748
|
+
if (!F.includes(e.getModuleSpecifierValue())) continue;
|
|
1748
1749
|
let r = e.getNamedImports().find((e) => e.getName() === "errorTag");
|
|
1749
1750
|
if (r === void 0) continue;
|
|
1750
1751
|
let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
|
|
1751
1752
|
r.remove();
|
|
1752
|
-
let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() ===
|
|
1753
|
+
let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === I && e.isTypeOnly() === a), s = {
|
|
1753
1754
|
name: "errorTag",
|
|
1754
1755
|
...i === void 0 ? {} : { alias: i }
|
|
1755
1756
|
};
|
|
1756
1757
|
o === void 0 ? t.addImportDeclaration({
|
|
1757
|
-
moduleSpecifier:
|
|
1758
|
+
moduleSpecifier: I,
|
|
1758
1759
|
isTypeOnly: a,
|
|
1759
1760
|
namedImports: [s]
|
|
1760
1761
|
}) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
|
|
1761
1762
|
}
|
|
1762
|
-
n > 0 && t(`errorTag: ${n} import(s) repointed to ${
|
|
1763
|
+
n > 0 && t(`errorTag: ${n} import(s) repointed to ${I}`);
|
|
1763
1764
|
}
|
|
1764
|
-
}),
|
|
1765
|
-
let t =
|
|
1766
|
-
return n === "index" ?
|
|
1765
|
+
}), L = /\.page\.tsx$/, Et = /\.page\.(test|spec)\.tsx$/, Dt = (e) => /(^|\/)src\/pages\//.test(e), Ot = (e) => {
|
|
1766
|
+
let t = _(e), n = g(e).replace(L, "");
|
|
1767
|
+
return n === "index" ? v(t, "page.tsx") : v(t, n, "page.tsx");
|
|
1767
1768
|
}, kt = (e) => {
|
|
1768
|
-
let t =
|
|
1769
|
-
return i === "index" ?
|
|
1770
|
-
}, At =
|
|
1769
|
+
let t = _(e), n = g(e), r = /\.page\.spec\.tsx$/.test(n) ? "spec" : "test", i = n.replace(Et, "");
|
|
1770
|
+
return i === "index" ? v(t, `page.${r}.tsx`) : v(t, i, `page.${r}.tsx`);
|
|
1771
|
+
}, At = T({
|
|
1771
1772
|
version: "0.17.0",
|
|
1772
1773
|
id: "0.17.0/01_pages-are-directories",
|
|
1773
1774
|
title: "A directory is a route segment; its route is page.tsx",
|
|
1774
1775
|
kind: "transform",
|
|
1775
1776
|
scope: ["**/src/pages/**/*.ts", "**/src/pages/**/*.tsx"],
|
|
1776
1777
|
needsImporters: !0,
|
|
1777
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Dt(e.getFilePath()) &&
|
|
1778
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Dt(e.getFilePath()) && L.test(g(e.getFilePath()))),
|
|
1778
1779
|
transform: ({ project: e, log: t }) => {
|
|
1779
1780
|
let n = e.getSourceFiles(), r = [];
|
|
1780
1781
|
for (let e of n) {
|
|
1781
1782
|
let t = String(e.getFilePath());
|
|
1782
1783
|
if (!Dt(t)) continue;
|
|
1783
|
-
let n =
|
|
1784
|
-
if (!i && !
|
|
1784
|
+
let n = g(t), i = Et.test(n);
|
|
1785
|
+
if (!i && !L.test(n)) continue;
|
|
1785
1786
|
let a = i ? kt(t) : Ot(t);
|
|
1786
1787
|
a !== t && r.push({
|
|
1787
1788
|
sf: e,
|
|
@@ -1801,7 +1802,7 @@ var x = (e, t, n) => {
|
|
|
1801
1802
|
}
|
|
1802
1803
|
}
|
|
1803
1804
|
let l = [], u = 0;
|
|
1804
|
-
for (let e of r) s.has(e.from) || (u +=
|
|
1805
|
+
for (let e of r) s.has(e.from) || (u += N(e.sf, e.to), e.isTest || l.push(o(e.to)));
|
|
1805
1806
|
if (l.length > 0 && (t(`moved ${l.length} route(s) into their own directory`), u > 0 && t(` rewrote ${u} aliased import specifier(s)`)), c.length > 0) {
|
|
1806
1807
|
t(""), t(`LEFT ALONE — ${c.length} case(s) where two files claim one route:`);
|
|
1807
1808
|
for (let e of c) t(` ${e}`);
|
|
@@ -1809,7 +1810,7 @@ var x = (e, t, n) => {
|
|
|
1809
1810
|
}
|
|
1810
1811
|
l.length === 0 && c.length === 0 && t("every route already lives in its own directory — nothing to move.");
|
|
1811
1812
|
}
|
|
1812
|
-
}), jt = /,\s*(['"])like\1\s*,/, Mt =
|
|
1813
|
+
}), jt = /,\s*(['"])like\1\s*,/, Mt = T({
|
|
1813
1814
|
version: "0.18.0",
|
|
1814
1815
|
id: "0.18.0/01_where-like-removed",
|
|
1815
1816
|
title: "the `'like'` where-operator is removed — pick `contains` or `startsWith`",
|
|
@@ -1852,7 +1853,7 @@ var x = (e, t, n) => {
|
|
|
1852
1853
|
"in a raw query (`ctx.store.raw`); the DSL has no arbitrary-pattern operator, and",
|
|
1853
1854
|
"adding one that cannot use an index is not on the roadmap."
|
|
1854
1855
|
].join("\n")
|
|
1855
|
-
}), Nt = /\.serverOnly\s*\(\s*\)/, Pt =
|
|
1856
|
+
}), Nt = /\.serverOnly\s*\(\s*\)/, Pt = T({
|
|
1856
1857
|
version: "0.19.0",
|
|
1857
1858
|
id: "0.19.0/01_server-only-gates-the-boot",
|
|
1858
1859
|
title: "`.serverOnly()` now FAILS the boot under `voltro serve` — it used to only warn",
|
|
@@ -1904,7 +1905,7 @@ var x = (e, t, n) => {
|
|
|
1904
1905
|
"hatch is that people delete the marker instead — and a check whose only way out",
|
|
1905
1906
|
"is to disable it gets disabled."
|
|
1906
1907
|
].join("\n")
|
|
1907
|
-
}), Ft = /\bauditPlugin\s*\(/, It =
|
|
1908
|
+
}), Ft = /\bauditPlugin\s*\(/, It = T({
|
|
1908
1909
|
version: "0.19.0",
|
|
1909
1910
|
id: "0.19.0/02_audit-redact-input-default",
|
|
1910
1911
|
title: "`auditPlugin` now redacts mutation input by default (`redactInput: 'all'`)",
|
|
@@ -1945,7 +1946,7 @@ var x = (e, t, n) => {
|
|
|
1945
1946
|
"those markers live on TABLE COLUMNS, and this is a mutation's INPUT.",
|
|
1946
1947
|
"`changePassword({ oldPassword, newPassword })` has no column to consult."
|
|
1947
1948
|
].join("\n")
|
|
1948
|
-
}), Lt =
|
|
1949
|
+
}), Lt = E("registerWriteRecorder"), Rt = T({
|
|
1949
1950
|
version: "0.20.0",
|
|
1950
1951
|
id: "0.20.0/01_write-recorder-port",
|
|
1951
1952
|
title: "`WriteRecorder` takes a port (`{ append, maxOf }`) instead of a bare `append`",
|
|
@@ -1989,7 +1990,7 @@ var x = (e, t, n) => {
|
|
|
1989
1990
|
"nested transaction, and a throw from either operation still rolls the",
|
|
1990
1991
|
"caller's write back. That is the guarantee the timing exists for."
|
|
1991
1992
|
].join("\n")
|
|
1992
|
-
}), zt = /\.maxLength\s*\(/, Bt =
|
|
1993
|
+
}), zt = /\.maxLength\s*\(/, Bt = T({
|
|
1993
1994
|
version: "0.21.0",
|
|
1994
1995
|
id: "0.21.0/01_maxlength-now-migrates",
|
|
1995
1996
|
title: "`text().maxLength(n)` now migrates EXISTING columns — plan before you apply",
|
|
@@ -2036,7 +2037,7 @@ var x = (e, t, n) => {
|
|
|
2036
2037
|
"framework guarantees, and an upgrade-time ALTER that fails on your data is a",
|
|
2037
2038
|
"worse outcome than the index-size concern it would fix."
|
|
2038
2039
|
].join("\n")
|
|
2039
|
-
}), Vt = /\bMigrationOperation\b|\bPlannedOperation\b|\bInspectMigrationOperationKind\b|\bOperationKind\b/, Ht =
|
|
2040
|
+
}), Vt = /\bMigrationOperation\b|\bPlannedOperation\b|\bInspectMigrationOperationKind\b|\bOperationKind\b/, Ht = T({
|
|
2040
2041
|
version: "0.22.0",
|
|
2041
2042
|
id: "0.22.0/01_rename-index-op",
|
|
2042
2043
|
title: "`MigrationOperation` gained a `rename-index` kind",
|
|
@@ -2073,7 +2074,7 @@ var x = (e, t, n) => {
|
|
|
2073
2074
|
"match where two same-shaped indexes were renamed at once. Those still plan",
|
|
2074
2075
|
"as drop + create, exactly as before."
|
|
2075
2076
|
].join("\n")
|
|
2076
|
-
}), Ut = /@voltro\/plugin-(notifications|scim|ai-flows)/, Wt =
|
|
2077
|
+
}), Ut = /@voltro\/plugin-(notifications|scim|ai-flows)/, Wt = T({
|
|
2077
2078
|
version: "0.22.0",
|
|
2078
2079
|
id: "0.22.0/02_plugin-tables-voltro-prefixed",
|
|
2079
2080
|
title: "plugin-owned tables are `_voltro_`-prefixed (the rows move themselves)",
|
|
@@ -2128,7 +2129,7 @@ var x = (e, t, n) => {
|
|
|
2128
2129
|
"gained a `rename-table` kind. An exhaustive `switch` needs an arm; don't add",
|
|
2129
2130
|
"a `default:`, which would silence this and every future kind."
|
|
2130
2131
|
].join("\n")
|
|
2131
|
-
}), Gt = /QueryCacheScope|QueryCacheConfig|scope\s*:\s*['"](?:subject|global)['"]/, Kt =
|
|
2132
|
+
}), Gt = /QueryCacheScope|QueryCacheConfig|scope\s*:\s*['"](?:subject|global)['"]/, Kt = T({
|
|
2132
2133
|
version: "0.22.0",
|
|
2133
2134
|
id: "0.22.0/03_cache-scope-gained-tenant",
|
|
2134
2135
|
title: "`cache.scope` gained 'tenant' — an exhaustive switch over it needs a branch",
|
|
@@ -2162,7 +2163,7 @@ var x = (e, t, n) => {
|
|
|
2162
2163
|
"Rubric: does the resolved predicate depend on the caller? On the PERSON →",
|
|
2163
2164
|
"`subject`; on their ORG only → `tenant`; on neither → `global`."
|
|
2164
2165
|
].join("\n")
|
|
2165
|
-
}), qt = /envTokenAuthResolver|InspectAuthResolver|authResolver/, Jt =
|
|
2166
|
+
}), qt = /envTokenAuthResolver|InspectAuthResolver|authResolver/, Jt = T({
|
|
2166
2167
|
version: "0.22.0",
|
|
2167
2168
|
id: "0.22.0/04_inspect-write-credential",
|
|
2168
2169
|
title: "Mutating `/_voltro/inspect/*` endpoints need VOLTRO_INSPECT_WRITE_TOKEN",
|
|
@@ -2205,7 +2206,7 @@ var x = (e, t, n) => {
|
|
|
2205
2206
|
"IMPLEMENTING a resolver is unaffected — `(headers) => …` still satisfies the",
|
|
2206
2207
|
"type. Only callers are obliged."
|
|
2207
2208
|
].join("\n")
|
|
2208
|
-
}), Yt = /versioningPlugin\s*\(/, Xt =
|
|
2209
|
+
}), Yt = /versioningPlugin\s*\(/, Xt = T({
|
|
2209
2210
|
version: "0.23.0",
|
|
2210
2211
|
id: "0.23.0/01_versioning-is-opt-out",
|
|
2211
2212
|
title: "versioningPlugin: `tables: string[]` → opt-out by default, `include` / `exclude` by value",
|
|
@@ -2250,7 +2251,7 @@ var x = (e, t, n) => {
|
|
|
2250
2251
|
"surprises you, `exclude` is the knob, and the retention sweep",
|
|
2251
2252
|
"(`VOLTRO_ROW_HISTORY_TTL_HOURS`) still bounds age."
|
|
2252
2253
|
].join("\n")
|
|
2253
|
-
}), Zt = /@voltro\/ai|\.agent\.tsx|agent_(threads|messages)/, Qt =
|
|
2254
|
+
}), Zt = /@voltro\/ai|\.agent\.tsx|agent_(threads|messages)/, Qt = T({
|
|
2254
2255
|
version: "0.24.0",
|
|
2255
2256
|
id: "0.24.0/01_agent-tables-voltro-prefixed",
|
|
2256
2257
|
title: "the two agent tables are `_voltro_`-prefixed (the rows move themselves)",
|
|
@@ -2308,7 +2309,7 @@ var x = (e, t, n) => {
|
|
|
2308
2309
|
"declared by something. You get a fresh empty `_voltro_…` table alongside it,",
|
|
2309
2310
|
"which is the collision this ends."
|
|
2310
2311
|
].join("\n")
|
|
2311
|
-
}), $t = /\.events\s*\.\s*emit\s*\(/, en =
|
|
2312
|
+
}), $t = /\.events\s*\.\s*emit\s*\(/, en = T({
|
|
2312
2313
|
version: "0.25.0",
|
|
2313
2314
|
id: "0.25.0/01_events-emit-to-publish",
|
|
2314
2315
|
title: "`ctx.events.emit(name, data)` → `ctx.events.publish(descriptor, key, payload)`",
|
|
@@ -2361,7 +2362,7 @@ var x = (e, t, n) => {
|
|
|
2361
2362
|
"description",
|
|
2362
2363
|
"version",
|
|
2363
2364
|
"globalRateLimit"
|
|
2364
|
-
]), nn = ["defaultRetry", "defaultSigning"], rn = (e) => e.getDescendantsOfKind(
|
|
2365
|
+
]), nn = ["defaultRetry", "defaultSigning"], rn = (e) => e.getDescendantsOfKind(S.CallExpression).filter((e) => e.getExpression().getText() === "defineOutgoingEvent"), an = T({
|
|
2365
2366
|
version: "0.25.0",
|
|
2366
2367
|
id: "0.25.0/02_outgoing-event-to-define-event",
|
|
2367
2368
|
title: "`defineOutgoingEvent` → `defineEvent({ …, webhook: { … } })`",
|
|
@@ -2371,8 +2372,8 @@ var x = (e, t, n) => {
|
|
|
2371
2372
|
let n = 0, r = /* @__PURE__ */ new Set(), i = [];
|
|
2372
2373
|
for (let t of e.getSourceFiles()) for (let e of rn(t)) {
|
|
2373
2374
|
let a = e.getArguments()[0];
|
|
2374
|
-
if (a === void 0 || !a.isKind(
|
|
2375
|
-
let o = a.getProperties().filter((e) => e.isKind(
|
|
2375
|
+
if (a === void 0 || !a.isKind(S.ObjectLiteralExpression)) continue;
|
|
2376
|
+
let o = a.getProperties().filter((e) => e.isKind(S.PropertyAssignment)), s = (e) => o.find((t) => t.asKindOrThrow(S.PropertyAssignment).getName() === e)?.asKindOrThrow(S.PropertyAssignment).getInitializer()?.getText(), c = s("id"), l = s("payload");
|
|
2376
2377
|
if (c === void 0 || l === void 0) continue;
|
|
2377
2378
|
let u = [...tn].map((e) => {
|
|
2378
2379
|
let t = s(e);
|
|
@@ -2384,26 +2385,26 @@ var x = (e, t, n) => {
|
|
|
2384
2385
|
}
|
|
2385
2386
|
n !== 0 && (t(`webhooks: rewrote ${n} \`defineOutgoingEvent\` → \`defineEvent\` in ${r.size} file${r.size === 1 ? "" : "s"}`), t(" → `key: Schema.Struct({})` is CORRECT, not a placeholder: a webhook event goes to"), t(" subscribed targets, not to a routing key. Give it a key only if you also want"), t(" connected clients to subscribe per-something with `useEvent`."), t(" → import `defineEvent` from `@voltro/protocol` and `Schema` from `effect`;"), t(" the file is now a declaration, so `*.event.ts` is its home."), t(" → `ctx.webhooks.emit(descriptor, payload)` still works, and"), t(" `ctx.events.publish(descriptor, key, payload)` now reaches the HTTP targets too."), i.length > 0 && (t(` → NOT carried across (${i.length}): ${[...new Set(i)].join(", ")}`), t(" The plugin's retry/signing shapes are richer than the declared event can hold,"), t(" so they are left in place as a compile error rather than dropped silently."), t(" Configure them at subscribe time, where the full shape is typed.")));
|
|
2386
2387
|
}
|
|
2387
|
-
}),
|
|
2388
|
+
}), R = (e) => e.getDescendantsOfKind(S.CallExpression).filter((e) => {
|
|
2388
2389
|
let t = e.getExpression().getText();
|
|
2389
2390
|
return t === "broadcast" || t === "broadcastPlugin";
|
|
2390
|
-
}),
|
|
2391
|
+
}), z = (e) => {
|
|
2391
2392
|
let t = e.getArguments()[0];
|
|
2392
|
-
if (!(t === void 0 || !t.isKind(
|
|
2393
|
-
},
|
|
2393
|
+
if (!(t === void 0 || !t.isKind(S.ObjectLiteralExpression))) return t.getProperties().filter((e) => e.isKind(S.PropertyAssignment)).map((e) => e.asKindOrThrow(S.PropertyAssignment)).find((e) => e.getName() === "channel");
|
|
2394
|
+
}, on = T({
|
|
2394
2395
|
version: "0.25.0",
|
|
2395
2396
|
id: "0.25.0/03_broadcast-channel-to-namespace",
|
|
2396
2397
|
title: "`broadcast({ channel })` → `broadcast({ namespace })`",
|
|
2397
2398
|
kind: "transform",
|
|
2398
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2399
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => R(e).some((e) => z(e) !== void 0)),
|
|
2399
2400
|
transform: ({ project: e, log: t }) => {
|
|
2400
2401
|
let n = 0, r = [], i = [], a = /* @__PURE__ */ new Set();
|
|
2401
|
-
for (let t of e.getSourceFiles()) for (let e of
|
|
2402
|
-
let o =
|
|
2402
|
+
for (let t of e.getSourceFiles()) for (let e of R(t)) {
|
|
2403
|
+
let o = z(e);
|
|
2403
2404
|
if (o === void 0) continue;
|
|
2404
2405
|
let s = o.getInitializer();
|
|
2405
2406
|
if (s === void 0) continue;
|
|
2406
|
-
let c = s.getText(), l = s.isKind(
|
|
2407
|
+
let c = s.getText(), l = s.isKind(S.StringLiteral) ? s.getLiteralValue() : void 0;
|
|
2407
2408
|
if (l !== void 0 && l.endsWith(":changes")) {
|
|
2408
2409
|
let e = l.slice(0, -8);
|
|
2409
2410
|
o.replaceWithText(`namespace: '${e}'`), r.push(`${t.getBaseName()}: '${l}' → '${e}'`);
|
|
@@ -2416,7 +2417,7 @@ var x = (e, t, n) => {
|
|
|
2416
2417
|
for (let e of i) t(` ! ${e} — carried verbatim; CHECK IT: the value is now a PREFIX the framework`), t(" appends ':changes' / ':events:<name>' / ':members' / ':presence' to.");
|
|
2417
2418
|
}
|
|
2418
2419
|
}
|
|
2419
|
-
}),
|
|
2420
|
+
}), sn = T({
|
|
2420
2421
|
version: "0.26.0",
|
|
2421
2422
|
id: "0.26.0/01_event-webhook-retry-removed",
|
|
2422
2423
|
title: "`defineEvent({ webhook: { retry } })` is gone — it never did anything",
|
|
@@ -2450,7 +2451,7 @@ var x = (e, t, n) => {
|
|
|
2450
2451
|
"If you were relying on the declared value to take effect, note that it never",
|
|
2451
2452
|
"did — your deliveries have been using the target default all along."
|
|
2452
2453
|
].join("\n")
|
|
2453
|
-
}),
|
|
2454
|
+
}), cn = T({
|
|
2454
2455
|
version: "0.26.0",
|
|
2455
2456
|
id: "0.26.0/02_step-retry-enforced",
|
|
2456
2457
|
title: "workflow step `retry:` is now ENFORCED (was metadata) — drop any redundant manual retry",
|
|
@@ -2503,12 +2504,12 @@ var x = (e, t, n) => {
|
|
|
2503
2504
|
"(retry transient, fail permanent), `maxElapsed: '5 minutes'` (a total time budget,",
|
|
2504
2505
|
"not just a count), `jitter` (on by default), and `respectRetryAfter` (honor a 429)."
|
|
2505
2506
|
].join("\n")
|
|
2506
|
-
}),
|
|
2507
|
+
}), ln = /\bmakeSubjectId\b/, un = T({
|
|
2507
2508
|
version: "0.28.0",
|
|
2508
2509
|
id: "0.28.0/01_notifications-resolve-subject-id-async",
|
|
2509
2510
|
title: "`makeSubjectId` returns a Promise; `resolveSubjectId` may be async",
|
|
2510
2511
|
kind: "manual",
|
|
2511
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2512
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ln.test(e.getFullText())),
|
|
2512
2513
|
note: [
|
|
2513
2514
|
"`makeSubjectId` from @voltro/plugin-notifications now returns a Promise.",
|
|
2514
2515
|
"",
|
|
@@ -2540,12 +2541,12 @@ var x = (e, t, n) => {
|
|
|
2540
2541
|
" reconnect. Memoise inside your own resolver if that trade suits your app —",
|
|
2541
2542
|
" you know your invalidation and we do not."
|
|
2542
2543
|
].join("\n")
|
|
2543
|
-
}),
|
|
2544
|
+
}), dn = /\bPluginRefChange\b|\bisSoftDelete\s*\(|\bapplyPluginRefRules\s*\(/, fn = T({
|
|
2544
2545
|
version: "0.28.0",
|
|
2545
2546
|
id: "0.28.0/02_plugin-ref-change-event",
|
|
2546
2547
|
title: "`PluginRefChange` is removed — the change channel's `ChangeEvent` is the one shape",
|
|
2547
2548
|
kind: "manual",
|
|
2548
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2549
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => dn.test(e.getFullText())),
|
|
2549
2550
|
note: [
|
|
2550
2551
|
"`PluginRefChange` no longer exists. `isSoftDelete` and `applyPluginRefRules`",
|
|
2551
2552
|
"take `ChangeEvent` from @voltro/database — the type the change channel actually",
|
|
@@ -2587,12 +2588,12 @@ var x = (e, t, n) => {
|
|
|
2587
2588
|
" not know that writes a delete, and the test passes against a stream the",
|
|
2588
2589
|
" framework never emits. That is the defect this change removes."
|
|
2589
2590
|
].join("\n")
|
|
2590
|
-
}),
|
|
2591
|
+
}), pn = /credentialsResolver\s*:/, mn = T({
|
|
2591
2592
|
version: "0.28.0",
|
|
2592
2593
|
id: "0.28.0/03_atlassian-credentials-context",
|
|
2593
2594
|
title: "`credentialsResolver` receives `{ subject, store }` instead of a bare Subject",
|
|
2594
2595
|
kind: "manual",
|
|
2595
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2596
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => pn.test(e.getFullText())),
|
|
2596
2597
|
note: [
|
|
2597
2598
|
"@voltro/plugin-atlassian: `credentialsResolver` now takes a context object.",
|
|
2598
2599
|
"",
|
|
@@ -2662,12 +2663,12 @@ var x = (e, t, n) => {
|
|
|
2662
2663
|
"",
|
|
2663
2664
|
"and rotate anything you find — a redacted future does not un-leak a past row."
|
|
2664
2665
|
].join("\n")
|
|
2665
|
-
}),
|
|
2666
|
+
}), hn = /auditPlugin\s*\(/, gn = T({
|
|
2666
2667
|
version: "0.28.0",
|
|
2667
2668
|
id: "0.28.0/04_audit-redacts-subject-metadata",
|
|
2668
2669
|
title: "`auditPlugin` redacts `subject.metadata` by default",
|
|
2669
2670
|
kind: "manual",
|
|
2670
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2671
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => hn.test(e.getFullText())),
|
|
2671
2672
|
note: [
|
|
2672
2673
|
"`auditPlugin` now redacts `subject.metadata` before the sink sees the event.",
|
|
2673
2674
|
"",
|
|
@@ -2723,12 +2724,12 @@ var x = (e, t, n) => {
|
|
|
2723
2724
|
" keeps the token in the framework vault, and the resolver's new `store`",
|
|
2724
2725
|
" handle covers an app with its own token table."
|
|
2725
2726
|
].join("\n")
|
|
2726
|
-
}),
|
|
2727
|
+
}), _n = E("useLoaderData"), vn = T({
|
|
2727
2728
|
version: "0.29.0",
|
|
2728
2729
|
id: "0.29.0/01_loader-data-absence",
|
|
2729
2730
|
title: "`useLoaderData()` throws at a level that declares no `loader`",
|
|
2730
2731
|
kind: "manual",
|
|
2731
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2732
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => _n.test(e.getFullText())),
|
|
2732
2733
|
note: [
|
|
2733
2734
|
"`useLoaderData()` used to return `undefined` at a level with no `loader`,",
|
|
2734
2735
|
"which then died at `Cannot destructure property 'x' of undefined` — a",
|
|
@@ -2764,38 +2765,38 @@ var x = (e, t, n) => {
|
|
|
2764
2765
|
"that legitimately resolves to `undefined` does not throw — only a missing",
|
|
2765
2766
|
"`loader` does."
|
|
2766
2767
|
].join("\n")
|
|
2767
|
-
}),
|
|
2768
|
+
}), B = "@voltro/ui-shadcn", V = "voltro:lang", yn = "voltro:locale", H = "parsePreferenceCookies", bn = (e, t) => {
|
|
2768
2769
|
let n = [];
|
|
2769
|
-
for (let r of e.getImportDeclarations()) if (r.getModuleSpecifierValue() ===
|
|
2770
|
+
for (let r of e.getImportDeclarations()) if (r.getModuleSpecifierValue() === B) for (let e of r.getNamedImports()) e.getName() === t && n.push(e.getAliasNode()?.getText() ?? e.getName());
|
|
2770
2771
|
return n;
|
|
2771
|
-
},
|
|
2772
|
+
}, xn = (e) => {
|
|
2772
2773
|
let t = 0;
|
|
2773
|
-
for (let n of e.getSourceFiles()) n.getFullText().includes(
|
|
2774
|
-
if (
|
|
2774
|
+
for (let n of e.getSourceFiles()) n.getFullText().includes(V) && n.forEachDescendant((e) => {
|
|
2775
|
+
if (x.isStringLiteral(e) || x.isNoSubstitutionTemplateLiteral(e) || x.isTemplateHead(e) || x.isTemplateMiddle(e) || x.isTemplateTail(e) || x.isRegularExpressionLiteral(e)) {
|
|
2775
2776
|
let n = e.getText();
|
|
2776
|
-
if (!n.includes(
|
|
2777
|
-
e.replaceWithText(n.split(
|
|
2777
|
+
if (!n.includes(V)) return;
|
|
2778
|
+
e.replaceWithText(n.split(V).join(yn)), t++;
|
|
2778
2779
|
}
|
|
2779
2780
|
});
|
|
2780
2781
|
return t;
|
|
2781
|
-
},
|
|
2782
|
+
}, Sn = (e) => {
|
|
2782
2783
|
let t = 0, n = 0;
|
|
2783
2784
|
for (let r of e.getSourceFiles()) {
|
|
2784
|
-
let e =
|
|
2785
|
+
let e = bn(r, H);
|
|
2785
2786
|
e.length !== 0 && r.forEachDescendant((r) => {
|
|
2786
|
-
if (!
|
|
2787
|
+
if (!x.isCallExpression(r) || !e.includes(r.getExpression().getText())) return;
|
|
2787
2788
|
let i = r.getParent();
|
|
2788
|
-
if (
|
|
2789
|
+
if (x.isPropertyAccessExpression(i) && i.getName() === "lang") {
|
|
2789
2790
|
i.getNameNode().replaceWithText("locale"), t++;
|
|
2790
2791
|
return;
|
|
2791
2792
|
}
|
|
2792
|
-
if (
|
|
2793
|
+
if (x.isVariableDeclaration(i)) {
|
|
2793
2794
|
let e = i.getNameNode();
|
|
2794
|
-
if (!
|
|
2795
|
+
if (!x.isObjectBindingPattern(e)) return;
|
|
2795
2796
|
for (let t of e.getElements()) {
|
|
2796
2797
|
if (t.getPropertyNameNode() !== void 0) continue;
|
|
2797
2798
|
let e = t.getNameNode();
|
|
2798
|
-
!
|
|
2799
|
+
!x.isIdentifier(e) || e.getText() !== "lang" || (t.replaceWithText(`locale: ${t.getText()}`), n++);
|
|
2799
2800
|
}
|
|
2800
2801
|
}
|
|
2801
2802
|
});
|
|
@@ -2804,29 +2805,29 @@ var x = (e, t, n) => {
|
|
|
2804
2805
|
access: t,
|
|
2805
2806
|
destructured: n
|
|
2806
2807
|
};
|
|
2807
|
-
},
|
|
2808
|
+
}, Cn = T({
|
|
2808
2809
|
version: "0.30.0",
|
|
2809
2810
|
id: "0.30.0/01_locale-cookie-rename",
|
|
2810
2811
|
title: "the language-preference cookie is `voltro:locale` (was `voltro:lang`)",
|
|
2811
2812
|
kind: "transform",
|
|
2812
2813
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2813
2814
|
let t = e.getFullText();
|
|
2814
|
-
return t.includes(
|
|
2815
|
+
return t.includes(V) || t.includes(B) && (t.includes("LANG_COOKIE") || t.includes(H));
|
|
2815
2816
|
}),
|
|
2816
2817
|
transform: ({ project: e, helpers: t, log: n }) => {
|
|
2817
2818
|
let r = t.renameImport(e, {
|
|
2818
|
-
module:
|
|
2819
|
+
module: B,
|
|
2819
2820
|
from: "LANG_COOKIE",
|
|
2820
2821
|
to: "LOCALE_COOKIE"
|
|
2821
|
-
}), i =
|
|
2822
|
+
}), i = xn(e), { access: a, destructured: o } = Sn(e), s = [];
|
|
2822
2823
|
r > 0 && s.push(`LANG_COOKIE → LOCALE_COOKIE (${r} import(s))`), i > 0 && s.push(`'voltro:lang' → 'voltro:locale' (${i} literal(s))`), a + o > 0 && s.push(`parsePreferenceCookies().lang → .locale (${a + o} site(s))`), s.length !== 0 && n(`locale cookie rename: ${s.join("; ")}. Your SOURCE is migrated. Your USERS' cookies are not — the old \`voltro:lang\` cookie in an already-visited browser is no longer read, so each user falls back to Accept-Language / defaultLocale once and re-picks their language. Nothing errors. If that reset is unacceptable, copy the old value forward at your own boot — the codemod file has the three-line bridge.` + (o > 0 ? ` ${o} destructure(s) were rewritten as \`{ locale: lang }\` to keep the local name safe from a scope collision — rename the binding yourself if you want the vocabulary to match.` : ""));
|
|
2823
2824
|
}
|
|
2824
|
-
}),
|
|
2825
|
+
}), wn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, Tn = T({
|
|
2825
2826
|
version: "0.30.0",
|
|
2826
2827
|
id: "0.30.0/02_audit-purge-query-recheck",
|
|
2827
2828
|
title: "re-run the credential-purge query — the 0.28.0 spelling under-reported on MySQL/MariaDB",
|
|
2828
2829
|
kind: "manual",
|
|
2829
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2830
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => wn.test(e.getFullText())),
|
|
2830
2831
|
note: [
|
|
2831
2832
|
"IF YOU ALREADY RAN THE 0.28.0 PURGE CHECK ON MySQL OR MariaDB, RUN IT AGAIN.",
|
|
2832
2833
|
"The query that note shipped under-reported, and it under-reported by",
|
|
@@ -2873,14 +2874,14 @@ var x = (e, t, n) => {
|
|
|
2873
2874
|
" `connectionCredentials(...)` keeps it in the framework vault",
|
|
2874
2875
|
" (see `0.28.0/03_atlassian-credentials-context`)."
|
|
2875
2876
|
].join("\n")
|
|
2876
|
-
}),
|
|
2877
|
+
}), En = /\.webhooks\s*\.\s*emit\s*\(|useWebhooks\s*\(/, Dn = T({
|
|
2877
2878
|
version: "0.30.0",
|
|
2878
2879
|
id: "0.30.0/03_webhook-emit-durable-deferral",
|
|
2879
2880
|
title: "a deferred `webhooks.emit` rides the transactional outbox; `{ immediate: true }` opts out",
|
|
2880
2881
|
kind: "manual",
|
|
2881
2882
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2882
2883
|
let t = e.getFullText();
|
|
2883
|
-
return
|
|
2884
|
+
return En.test(t) || E("emit").test(t);
|
|
2884
2885
|
}),
|
|
2885
2886
|
note: [
|
|
2886
2887
|
"A `ctx.webhooks.emit(...)` inside a MUTATION now enqueues a transactional",
|
|
@@ -2930,14 +2931,14 @@ var x = (e, t, n) => {
|
|
|
2930
2931
|
"created by the declarative differ on the next `voltro dev` boot or",
|
|
2931
2932
|
"`voltro db apply` — there is nothing to run by hand."
|
|
2932
2933
|
].join("\n")
|
|
2933
|
-
}),
|
|
2934
|
+
}), On = /\.executionId\b|workflows\s*\.\s*start\s*\(|workflows\s*\.\s*child\s*\(/, kn = T({
|
|
2934
2935
|
version: "0.30.0",
|
|
2935
2936
|
id: "0.30.0/04_workflow-run-handle-nullable-execution",
|
|
2936
2937
|
title: "`WorkflowRunHandle.executionId` is `string | null`; `status` gains queued/dropped/skipped",
|
|
2937
2938
|
kind: "manual",
|
|
2938
2939
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2939
2940
|
let t = e.getFullText();
|
|
2940
|
-
return
|
|
2941
|
+
return On.test(t) || E("start").test(t);
|
|
2941
2942
|
}),
|
|
2942
2943
|
note: [
|
|
2943
2944
|
"A workflow start no longer always becomes a run. With declarative flow",
|
|
@@ -3002,12 +3003,12 @@ var x = (e, t, n) => {
|
|
|
3002
3003
|
" and `singleton: { mode: 'skip' }` throws `WorkflowSingletonHeldError`",
|
|
3003
3004
|
" carrying the incumbent's execution id, so you can wait on THAT run."
|
|
3004
3005
|
].join("\n")
|
|
3005
|
-
}),
|
|
3006
|
+
}), An = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, jn = T({
|
|
3006
3007
|
version: "0.31.0",
|
|
3007
3008
|
id: "0.31.0/01_rerun-credential-scan",
|
|
3008
3009
|
title: "re-run `voltro db scan-credentials` — on 0.30.0–0.30.2 it skipped the column credentials land in",
|
|
3009
3010
|
kind: "manual",
|
|
3010
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3011
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => An.test(e.getFullText())),
|
|
3011
3012
|
note: [
|
|
3012
3013
|
"IF YOU RAN `voltro db scan-credentials` ON 0.30.0, 0.30.1 OR 0.30.2, RUN IT",
|
|
3013
3014
|
"AGAIN. Its answer did not cover the column a leaked credential is most",
|
|
@@ -3041,7 +3042,7 @@ var x = (e, t, n) => {
|
|
|
3041
3042
|
"credential on the Subject — `connectionCredentials(...)` keeps it in the",
|
|
3042
3043
|
"framework vault."
|
|
3043
3044
|
].join("\n")
|
|
3044
|
-
}),
|
|
3045
|
+
}), Mn = T({
|
|
3045
3046
|
version: "0.32.0",
|
|
3046
3047
|
id: "0.32.0/01_audit-redacts-outcome",
|
|
3047
3048
|
title: "`auditPlugin` now redacts `outcome` by default — opt out if your trail reads it",
|
|
@@ -3082,7 +3083,7 @@ var x = (e, t, n) => {
|
|
|
3082
3083
|
"purge the rows AND rotate the credentials — assume anything written to a",
|
|
3083
3084
|
"log table has been read."
|
|
3084
3085
|
].join("\n")
|
|
3085
|
-
}),
|
|
3086
|
+
}), Nn = T({
|
|
3086
3087
|
version: "0.33.0",
|
|
3087
3088
|
id: "0.33.0/01_opentelemetry-optional-peer",
|
|
3088
3089
|
title: "`@effect/opentelemetry` is now an optional peer — install it if you use tracing",
|
|
@@ -3114,7 +3115,7 @@ var x = (e, t, n) => {
|
|
|
3114
3115
|
"never wrote down. It is a startup failure, not a silent loss of traces —",
|
|
3115
3116
|
"losing telemetry quietly would be the worse outcome."
|
|
3116
3117
|
].join("\n")
|
|
3117
|
-
}),
|
|
3118
|
+
}), Pn = T({
|
|
3118
3119
|
version: "0.33.0",
|
|
3119
3120
|
id: "0.33.0/02_coordinated-handle-gained-wake",
|
|
3120
3121
|
title: "`CoordinatedScheduleHandle` gained `wake()`, `currentIntervalMs()` and `isArmed()` — update hand-written test doubles",
|
|
@@ -3164,7 +3165,7 @@ var x = (e, t, n) => {
|
|
|
3164
3165
|
"Reporting nothing is the conservative default on purpose — a task that",
|
|
3165
3166
|
"cannot tell whether it was idle must not be slowed down on a guess."
|
|
3166
3167
|
].join("\n")
|
|
3167
|
-
}),
|
|
3168
|
+
}), Fn = T({
|
|
3168
3169
|
version: "0.34.0",
|
|
3169
3170
|
id: "0.34.0/01_serve-does-mount-inspect",
|
|
3170
3171
|
title: "Correction: `voltro serve` DOES mount an inspect surface (the 0.12.0 note said it mounts none)",
|
|
@@ -3205,12 +3206,12 @@ var x = (e, t, n) => {
|
|
|
3205
3206
|
"place, everyone already past 0.12.0 — which is everyone the wrong sentence",
|
|
3206
3207
|
"could have misled — would never have seen it."
|
|
3207
3208
|
].join("\n")
|
|
3208
|
-
}),
|
|
3209
|
+
}), In = (e) => /\.with\(\s*[\w.]*tenant\(/.test(e) || /\.serverOnly\(\s*\)/.test(e), Ln = T({
|
|
3209
3210
|
version: "0.34.0",
|
|
3210
3211
|
id: "0.34.0/02_keyed-writes-are-tenant-scoped",
|
|
3211
3212
|
title: "Keyed-by-pk writes are tenant-enforced, and `crud` refuses `.serverOnly()` input",
|
|
3212
3213
|
kind: "manual",
|
|
3213
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3214
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => In(e.getFullText())),
|
|
3214
3215
|
note: [
|
|
3215
3216
|
"TWO WRITE-SIDE REFUSALS ARE NEW. Neither changes a type, so `tsc` will not",
|
|
3216
3217
|
"point at the call sites — these are runtime behaviour changes.",
|
|
@@ -3274,30 +3275,30 @@ var x = (e, t, n) => {
|
|
|
3274
3275
|
" an attack look like a no-op and leaves an honest caller debugging a",
|
|
3275
3276
|
" value that quietly did not land."
|
|
3276
3277
|
].join("\n")
|
|
3277
|
-
}),
|
|
3278
|
+
}), Rn = /* @__PURE__ */ new Set([
|
|
3278
3279
|
"defineQuery",
|
|
3279
3280
|
"defineMutation",
|
|
3280
3281
|
"defineAction",
|
|
3281
3282
|
"defineStream"
|
|
3282
|
-
]),
|
|
3283
|
+
]), zn = (e) => {
|
|
3283
3284
|
let t = e.getArguments()[0];
|
|
3284
|
-
if (t === void 0 || !t.isKind(
|
|
3285
|
+
if (t === void 0 || !t.isKind(S.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
|
|
3285
3286
|
let n = /* @__PURE__ */ new Set();
|
|
3286
3287
|
for (let e of t.getProperties()) {
|
|
3287
|
-
let t = (e.asKind(
|
|
3288
|
+
let t = (e.asKind(S.PropertyAssignment) ?? e.asKind(S.ShorthandPropertyAssignment))?.getName?.();
|
|
3288
3289
|
t !== void 0 && n.add(t);
|
|
3289
3290
|
}
|
|
3290
3291
|
return n;
|
|
3291
|
-
},
|
|
3292
|
-
if (!
|
|
3293
|
-
let t =
|
|
3292
|
+
}, Bn = (e, t) => /define(Query|Mutation|Action|Stream)\s*\(/.test(e) ? t.some((e) => {
|
|
3293
|
+
if (!Rn.has(e.getExpression().getText())) return !1;
|
|
3294
|
+
let t = zn(e);
|
|
3294
3295
|
return !t.has("internal") && !t.has("guards") && !t.has("openAccess");
|
|
3295
|
-
}) : !1,
|
|
3296
|
+
}) : !1, Vn = T({
|
|
3296
3297
|
version: "0.34.0",
|
|
3297
3298
|
id: "0.34.0/03_procedure-access-decision",
|
|
3298
3299
|
title: "Every wire-exposed procedure declares an access decision (`guards:` or `openAccess:`)",
|
|
3299
3300
|
kind: "manual",
|
|
3300
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3301
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Bn(e.getFullText(), e.getDescendantsOfKind(S.CallExpression))),
|
|
3301
3302
|
note: [
|
|
3302
3303
|
"YOUR APP WILL NOT BOOT UNTIL EVERY WIRE-EXPOSED PROCEDURE DECIDES WHO MAY",
|
|
3303
3304
|
"CALL IT. This is the single largest behaviour change in this release, and it",
|
|
@@ -3342,18 +3343,18 @@ var x = (e, t, n) => {
|
|
|
3342
3343
|
"procedures your PLUGINS declare — the gate reads your app's own discovered",
|
|
3343
3344
|
"files only, so adopting this does not turn into a plugin bug report."
|
|
3344
3345
|
].join("\n")
|
|
3345
|
-
}),
|
|
3346
|
+
}), Hn = [
|
|
3346
3347
|
"workosAuthorizationUrl",
|
|
3347
3348
|
"workosAuthenticateWithCode",
|
|
3348
3349
|
"workosBeginLogin"
|
|
3349
|
-
],
|
|
3350
|
+
], Un = T({
|
|
3350
3351
|
version: "0.34.0",
|
|
3351
3352
|
id: "0.34.0/04_workos-oauth-state-pkce",
|
|
3352
3353
|
title: "WorkOS hosted login: `workosAuthorizationUrl` → `workosBeginLogin`, with mandatory `state` + PKCE",
|
|
3353
3354
|
kind: "manual",
|
|
3354
3355
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
3355
3356
|
let t = e.getFullText();
|
|
3356
|
-
return t.includes("@voltro/plugin-auth-workos") &&
|
|
3357
|
+
return t.includes("@voltro/plugin-auth-workos") && Hn.some((e) => t.includes(e));
|
|
3357
3358
|
}),
|
|
3358
3359
|
note: [
|
|
3359
3360
|
"YOUR WORKOS HOSTED-LOGIN FLOW HAD NO CSRF PROTECTION BY DEFAULT, AND NO PKCE.",
|
|
@@ -3411,12 +3412,12 @@ var x = (e, t, n) => {
|
|
|
3411
3412
|
"are accepted on the existing endpoints; your registered redirect URI is",
|
|
3412
3413
|
"unchanged."
|
|
3413
3414
|
].join("\n")
|
|
3414
|
-
}),
|
|
3415
|
+
}), Wn = /mirrorTables|_?voltro_mirror_/, Gn = T({
|
|
3415
3416
|
version: "0.34.0",
|
|
3416
3417
|
id: "0.34.0/05_analytics-mirror-versioned-writes",
|
|
3417
3418
|
title: "Analytics CDC-mirror: version-ordered writes, tombstoned deletes, retry + repair",
|
|
3418
3419
|
kind: "manual",
|
|
3419
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3420
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Wn.test(e.getFullText())),
|
|
3420
3421
|
note: [
|
|
3421
3422
|
"The analytics CDC-mirror (`mirrorTables`) now orders every write and",
|
|
3422
3423
|
"retries the ones that fail. Three things to do.",
|
|
@@ -3471,7 +3472,7 @@ var x = (e, t, n) => {
|
|
|
3471
3472
|
" VOLTRO_ANALYTICS_MIRROR_REPAIR_INTERVAL_MS (60000, 0 disables)",
|
|
3472
3473
|
" VOLTRO_ANALYTICS_MIRROR_REPAIR_QUEUE_LIMIT (10000)"
|
|
3473
3474
|
].join("\n")
|
|
3474
|
-
}),
|
|
3475
|
+
}), Kn = T({
|
|
3475
3476
|
version: "0.34.0",
|
|
3476
3477
|
id: "0.34.0/06_origin-guard-and-trusted-proxies",
|
|
3477
3478
|
title: "Cross-site protection, trusted proxies and security headers are ON by default on the api listener",
|
|
@@ -3595,7 +3596,7 @@ var x = (e, t, n) => {
|
|
|
3595
3596
|
" a `Transfer-Encoding: chunked` body skipped it and was buffered without",
|
|
3596
3597
|
" bound. No action needed; VOLTRO_MAX_RPC_BODY_BYTES still tunes it."
|
|
3597
3598
|
].join("\n")
|
|
3598
|
-
}),
|
|
3599
|
+
}), qn = T({
|
|
3599
3600
|
version: "0.34.0",
|
|
3600
3601
|
id: "0.34.0/07_incoming-webhook-declares-verification",
|
|
3601
3602
|
title: "An incoming webhook with no signature scheme now refuses to boot",
|
|
@@ -3644,7 +3645,7 @@ var x = (e, t, n) => {
|
|
|
3644
3645
|
"value — the sender holds the other half of it, so a generated one would",
|
|
3645
3646
|
"authenticate nobody."
|
|
3646
3647
|
].join("\n")
|
|
3647
|
-
}),
|
|
3648
|
+
}), Jn = T({
|
|
3648
3649
|
version: "0.34.0",
|
|
3649
3650
|
id: "0.34.0/08_init-initialises-the-workspace",
|
|
3650
3651
|
title: "`voltro init` initialises the workspace root; scaffolding is `create-project` only",
|
|
@@ -3696,20 +3697,20 @@ var x = (e, t, n) => {
|
|
|
3696
3697
|
"all 18 of its scripts — including the `pnpm db:up` the CLI then printed as a",
|
|
3697
3698
|
"next step. Run `voltro init` in the root first."
|
|
3698
3699
|
].join("\n")
|
|
3699
|
-
}),
|
|
3700
|
+
}), Yn = (e, t) => (e.getProperty(t)?.asKind(S.PropertyAssignment))?.getInitializer()?.asKind(S.ObjectLiteralExpression), Xn = (e) => {
|
|
3700
3701
|
if (e.getExpression().getText() !== "workflow") return !1;
|
|
3701
|
-
let t = e.getArguments()[0]?.asKind(
|
|
3702
|
+
let t = e.getArguments()[0]?.asKind(S.ObjectLiteralExpression);
|
|
3702
3703
|
if (t === void 0) return !1;
|
|
3703
|
-
let n =
|
|
3704
|
+
let n = Yn(t, "messages");
|
|
3704
3705
|
return n !== void 0 && n.getProperty("queries") !== void 0;
|
|
3705
|
-
},
|
|
3706
|
+
}, Zn = T({
|
|
3706
3707
|
version: "0.34.0",
|
|
3707
3708
|
id: "0.34.0/09_workflow-messages-queries-removed",
|
|
3708
3709
|
title: "`workflow({ messages: { queries } })` is removed — it never had a send path",
|
|
3709
3710
|
kind: "manual",
|
|
3710
3711
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
3711
3712
|
let t = e.getFullText();
|
|
3712
|
-
return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(
|
|
3713
|
+
return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(S.CallExpression).some(Xn);
|
|
3713
3714
|
}),
|
|
3714
3715
|
note: [
|
|
3715
3716
|
"YOU DECLARED `messages: { queries: { … } }` ON A WORKFLOW. That channel is",
|
|
@@ -3752,7 +3753,7 @@ var x = (e, t, n) => {
|
|
|
3752
3753
|
"in-flight runs finish on the old path after a deploy. See the Versioning",
|
|
3753
3754
|
"page in the docs."
|
|
3754
3755
|
].join("\n")
|
|
3755
|
-
}),
|
|
3756
|
+
}), Qn = T({
|
|
3756
3757
|
version: "0.34.0",
|
|
3757
3758
|
id: "0.34.0/11_ai-flows-per-step-human-signal",
|
|
3758
3759
|
title: "`HUMAN_RESPONSE_SIGNAL` removed — a human step's signal name is now per-step",
|
|
@@ -3796,23 +3797,23 @@ var x = (e, t, n) => {
|
|
|
3796
3797
|
"per flow (`humanTimeoutMs`), per app (`aiFlowsPlugin({ humanReviewTimeoutMs })`)",
|
|
3797
3798
|
"or per deploy (`VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS`). `0` = wait forever."
|
|
3798
3799
|
].join("\n")
|
|
3799
|
-
}),
|
|
3800
|
+
}), $n = /* @__PURE__ */ new Set([
|
|
3800
3801
|
"@voltro/plugin-auth",
|
|
3801
3802
|
"@voltro/protocol/session",
|
|
3802
3803
|
"@voltro/plugin-sso-saml"
|
|
3803
|
-
]),
|
|
3804
|
+
]), er = /* @__PURE__ */ new Set(["issueSession", "signSession"]), tr = (e) => {
|
|
3804
3805
|
let t = e.getFullText();
|
|
3805
3806
|
if (!/issueSession|signSession|resolveScopes|@voltro\/(plugin-auth|protocol\/session|plugin-sso-saml)/.test(t)) return !1;
|
|
3806
|
-
for (let t of e.getImportDeclarations()) if (
|
|
3807
|
-
for (let t of e.getDescendantsOfKind(
|
|
3808
|
-
for (let t of e.getDescendantsOfKind(
|
|
3807
|
+
for (let t of e.getImportDeclarations()) if ($n.has(t.getModuleSpecifierValue())) return !0;
|
|
3808
|
+
for (let t of e.getDescendantsOfKind(S.CallExpression)) if (er.has(t.getExpression().getText())) return !0;
|
|
3809
|
+
for (let t of e.getDescendantsOfKind(S.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
|
|
3809
3810
|
return !1;
|
|
3810
|
-
},
|
|
3811
|
+
}, nr = T({
|
|
3811
3812
|
version: "0.34.0",
|
|
3812
3813
|
id: "0.34.0/12_session-cookie-carries-identity",
|
|
3813
3814
|
title: "The session cookie carries identity only — authority resolves per request",
|
|
3814
3815
|
kind: "manual",
|
|
3815
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
3816
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(tr),
|
|
3816
3817
|
note: [
|
|
3817
3818
|
"YOUR USERS WILL SIGN IN ONCE MORE, AND A SESSION COOKIE CAN NO LONGER",
|
|
3818
3819
|
"CARRY PERMISSIONS.",
|
|
@@ -3903,12 +3904,12 @@ var x = (e, t, n) => {
|
|
|
3903
3904
|
"not have: an authoritative resolver now overrides a token's own scope",
|
|
3904
3905
|
"claim, which union-only could never do."
|
|
3905
3906
|
].join("\n")
|
|
3906
|
-
}),
|
|
3907
|
+
}), rr = /\bMockEmail\b|\.email\.(sent|lastTo|clear)\b|\bSentEmail\b/, ir = T({
|
|
3907
3908
|
version: "0.34.0",
|
|
3908
3909
|
id: "0.34.0/10_testing-mail-is-a-service",
|
|
3909
3910
|
title: "`ctx.email` / `MockEmail` removed — assert mail through the mail plugin",
|
|
3910
3911
|
kind: "manual",
|
|
3911
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3912
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => rr.test(e.getFullText())),
|
|
3912
3913
|
note: [
|
|
3913
3914
|
"`@voltro/testing` no longer ships `MockEmail`, and `makeTestContext()` no",
|
|
3914
3915
|
"longer returns `ctx.email`. Your tests that touch it will not compile.",
|
|
@@ -3977,12 +3978,12 @@ var x = (e, t, n) => {
|
|
|
3977
3978
|
" • `makeTestContext({ layers })` — your app's own `app.config.ts`",
|
|
3978
3979
|
" `layers:` now resolve for an Effect-mode handler under test."
|
|
3979
3980
|
].join("\n")
|
|
3980
|
-
}),
|
|
3981
|
+
}), ar = /from\s+['"]@voltro\/plugin-billing(?:\/[\w-]+)?['"]/, or = T({
|
|
3981
3982
|
version: "0.34.0",
|
|
3982
3983
|
id: "0.34.0/13_billing-provider-dunning",
|
|
3983
3984
|
title: "Billing dunning — `BillingProvider.fetchSubscription` is required, and `plan()` now honours a grace period",
|
|
3984
3985
|
kind: "manual",
|
|
3985
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3986
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ar.test(e.getFullText())),
|
|
3986
3987
|
note: [
|
|
3987
3988
|
"BILLING GAINED A DUNNING SEQUENCE. TWO THINGS CHANGED UNDER YOU.",
|
|
3988
3989
|
"",
|
|
@@ -4058,12 +4059,12 @@ var x = (e, t, n) => {
|
|
|
4058
4059
|
"`pastDueSince` / `statusEventAt` columns ride the declarative differ — they",
|
|
4059
4060
|
"are applied by `voltro db apply` and by a `voltro dev` boot, on every dialect."
|
|
4060
4061
|
].join("\n")
|
|
4061
|
-
}),
|
|
4062
|
+
}), sr = /from\s+['"]@voltro\/plugin-search(?:\/[\w-]+)?['"]/, cr = T({
|
|
4062
4063
|
version: "0.34.0",
|
|
4063
4064
|
id: "0.34.0/14_search-memory-backend-refuses-production",
|
|
4064
4065
|
title: "`plugin-search` on the in-memory backend now refuses to boot in production",
|
|
4065
4066
|
kind: "manual",
|
|
4066
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4067
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => sr.test(e.getFullText())),
|
|
4067
4068
|
note: [
|
|
4068
4069
|
"THE ZERO-CONFIG SEARCH BACKEND NO LONGER BOOTS IN PRODUCTION.",
|
|
4069
4070
|
"",
|
|
@@ -4132,7 +4133,7 @@ var x = (e, t, n) => {
|
|
|
4132
4133
|
"(`ts_headline` / `snippet()` / …) that `SearchQuery` promises. Degrading",
|
|
4133
4134
|
"those silently would replace one lie with another."
|
|
4134
4135
|
].join("\n")
|
|
4135
|
-
}),
|
|
4136
|
+
}), lr = T({
|
|
4136
4137
|
version: "0.34.0",
|
|
4137
4138
|
id: "0.34.0/15_mysql-mssql-tls-honoured",
|
|
4138
4139
|
title: "mysql/mariadb and mssql connections honour ?ssl= / ?sslmode= — or refuse to boot",
|
|
@@ -4204,7 +4205,7 @@ var x = (e, t, n) => {
|
|
|
4204
4205
|
"spins in-tick and starves the event loop. Set it only if you know nothing in",
|
|
4205
4206
|
"your process does that, and set it well above their peak concurrency."
|
|
4206
4207
|
].join("\n")
|
|
4207
|
-
}),
|
|
4208
|
+
}), ur = T({
|
|
4208
4209
|
version: "0.34.0",
|
|
4209
4210
|
id: "0.34.0/16_declare-node-env-for-migration-commands",
|
|
4210
4211
|
title: "`voltro db apply` / `voltro migrate` now treat an unset NODE_ENV as production",
|
|
@@ -4268,12 +4269,12 @@ var x = (e, t, n) => {
|
|
|
4268
4269
|
"process environment if you want a non-production `voltro serve` without a",
|
|
4269
4270
|
"built bundle. Everything downstream of the launcher does honour `.env`."
|
|
4270
4271
|
].join("\n")
|
|
4271
|
-
}),
|
|
4272
|
+
}), dr = /\/(?:src\/)?pages\//, fr = /\bcacheInvalidatesOn\b/, pr = T({
|
|
4272
4273
|
version: "0.34.0",
|
|
4273
4274
|
id: "0.34.0/17_isr-postgres-refuses-without-a-database",
|
|
4274
4275
|
title: "The postgres ISR cache + CDC invalidator read DB_URL, and no longer fall back silently",
|
|
4275
4276
|
kind: "manual",
|
|
4276
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4277
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => fr.test(e.getFullText()) || dr.test(e.getFilePath())),
|
|
4277
4278
|
note: [
|
|
4278
4279
|
"YOUR WEB PROCESS MAY HAVE BEEN USING THE MEMORY ISR CACHE ALL ALONG.",
|
|
4279
4280
|
"",
|
|
@@ -4318,12 +4319,12 @@ var x = (e, t, n) => {
|
|
|
4318
4319
|
" until their `revalidate` window expires. Give the web process a `DB_URL`",
|
|
4319
4320
|
" or remove the declaration."
|
|
4320
4321
|
].join("\n")
|
|
4321
|
-
}),
|
|
4322
|
+
}), mr = /\.migration\.ts$/, hr = T({
|
|
4322
4323
|
version: "0.34.0",
|
|
4323
4324
|
id: "0.34.0/18_serve-refuses-pending-file-migrations",
|
|
4324
4325
|
title: "`voltro serve` refuses to boot with pending file-based migrations",
|
|
4325
4326
|
kind: "manual",
|
|
4326
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4327
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => mr.test(e.getFilePath())),
|
|
4327
4328
|
note: [
|
|
4328
4329
|
"YOUR DEPLOY MUST RUN `migrations/*.migration.ts` BEFORE `voltro serve`.",
|
|
4329
4330
|
"",
|
|
@@ -4366,11 +4367,11 @@ var x = (e, t, n) => {
|
|
|
4366
4367
|
"fingerprint check — it is the one switch for \"no boot-time schema checks\",",
|
|
4367
4368
|
"and it now covers both."
|
|
4368
4369
|
].join("\n")
|
|
4369
|
-
}),
|
|
4370
|
+
}), gr = /* @__PURE__ */ new Set([
|
|
4370
4371
|
"@voltro/plugin-auth",
|
|
4371
4372
|
"@voltro/plugin-auth/schema",
|
|
4372
4373
|
"@voltro/plugin-auth/plugin"
|
|
4373
|
-
]),
|
|
4374
|
+
]), _r = /* @__PURE__ */ new Set([
|
|
4374
4375
|
"UserStore",
|
|
4375
4376
|
"authTables",
|
|
4376
4377
|
"SendEmail",
|
|
@@ -4378,20 +4379,20 @@ var x = (e, t, n) => {
|
|
|
4378
4379
|
"memoryUserStore",
|
|
4379
4380
|
"postgresUserStore",
|
|
4380
4381
|
"authRoutesPlugin"
|
|
4381
|
-
]),
|
|
4382
|
+
]), vr = (e) => {
|
|
4382
4383
|
if (!e.getFullText().includes("@voltro/plugin-auth")) return !1;
|
|
4383
|
-
for (let t of e.getImportDeclarations()) if (
|
|
4384
|
-
for (let e of t.getNamedImports()) if (
|
|
4384
|
+
for (let t of e.getImportDeclarations()) if (gr.has(t.getModuleSpecifierValue())) {
|
|
4385
|
+
for (let e of t.getNamedImports()) if (_r.has(e.getName())) return !0;
|
|
4385
4386
|
if (t.getNamespaceImport() !== void 0) return !0;
|
|
4386
4387
|
}
|
|
4387
|
-
for (let t of e.getDescendantsOfKind(
|
|
4388
|
+
for (let t of e.getDescendantsOfKind(S.TypeReference)) if (t.getTypeName().getText() === "UserStore") return !0;
|
|
4388
4389
|
return !1;
|
|
4389
|
-
},
|
|
4390
|
+
}, yr = T({
|
|
4390
4391
|
version: "0.34.0",
|
|
4391
4392
|
id: "0.34.0/20_user-store-auth-feature-methods",
|
|
4392
4393
|
title: "UserStore gained the email-verification, invitation and impersonation methods",
|
|
4393
4394
|
kind: "manual",
|
|
4394
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4395
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(vr),
|
|
4395
4396
|
note: [
|
|
4396
4397
|
"IF YOU USE memoryUserStore OR postgresUserStore, THERE IS NOTHING TO DO.",
|
|
4397
4398
|
"Both already implement everything below. Read on only if you wrote your",
|
|
@@ -4471,26 +4472,26 @@ var x = (e, t, n) => {
|
|
|
4471
4472
|
" is the difference between \"new signups must confirm\" and \"everyone is",
|
|
4472
4473
|
" locked out until they check their mail\"."
|
|
4473
4474
|
].join("\n")
|
|
4474
|
-
}),
|
|
4475
|
+
}), U = /* @__PURE__ */ new Set([
|
|
4475
4476
|
"bindConnectionSubject",
|
|
4476
4477
|
"getConnectionSubject",
|
|
4477
4478
|
"unbindConnectionSubject",
|
|
4478
4479
|
"onBindConnectionSubject",
|
|
4479
4480
|
"connectionSubjectsSnapshot"
|
|
4480
|
-
]),
|
|
4481
|
+
]), br = (e) => {
|
|
4481
4482
|
let t = e.getFullText();
|
|
4482
4483
|
if (!/ConnectionSubject|connectionSubjectsSnapshot|rebind/.test(t)) return !1;
|
|
4483
|
-
for (let t of e.getDescendantsOfKind(
|
|
4484
|
-
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (
|
|
4485
|
-
for (let t of e.getDescendantsOfKind(
|
|
4486
|
-
for (let t of e.getDescendantsOfKind(
|
|
4484
|
+
for (let t of e.getDescendantsOfKind(S.CallExpression)) if (U.has(t.getExpression().getText())) return !0;
|
|
4485
|
+
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (U.has(e.getName())) return !0;
|
|
4486
|
+
for (let t of e.getDescendantsOfKind(S.PropertyAssignment)) if (t.getName() === "rebind") return !0;
|
|
4487
|
+
for (let t of e.getDescendantsOfKind(S.ShorthandPropertyAssignment)) if (t.getName() === "rebind") return !0;
|
|
4487
4488
|
return !1;
|
|
4488
|
-
},
|
|
4489
|
+
}, xr = T({
|
|
4489
4490
|
version: "0.34.0",
|
|
4490
4491
|
id: "0.34.0/19_connection-override-carries-a-credential",
|
|
4491
4492
|
title: "Soft re-auth presents a credential — it no longer hands the server a Subject",
|
|
4492
4493
|
kind: "manual",
|
|
4493
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4494
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(br),
|
|
4494
4495
|
note: [
|
|
4495
4496
|
"A LIVE CONNECTION CAN NO LONGER BE HANDED A SUBJECT. IT PRESENTS A",
|
|
4496
4497
|
"CREDENTIAL, AND THE SERVER RESOLVES IT LIKE ANY OTHER REQUEST.",
|
|
@@ -4575,19 +4576,19 @@ var x = (e, t, n) => {
|
|
|
4575
4576
|
"it behaves identically under `voltro dev` and `voltro serve`, because both",
|
|
4576
4577
|
"now build the middleware from one function (`makeAuthMiddlewareLayers`)."
|
|
4577
4578
|
].join("\n")
|
|
4578
|
-
}),
|
|
4579
|
+
}), Sr = (e) => {
|
|
4579
4580
|
if (/\.workflow(\.server)?\.tsx?$/.test(e.getBaseName())) return !0;
|
|
4580
4581
|
let t = e.getFullText();
|
|
4581
4582
|
if (!/resolveScopes|defineWorkflow|ctx\.workflows/.test(t)) return !1;
|
|
4582
|
-
for (let t of e.getDescendantsOfKind(
|
|
4583
|
-
for (let t of e.getDescendantsOfKind(
|
|
4583
|
+
for (let t of e.getDescendantsOfKind(S.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
|
|
4584
|
+
for (let t of e.getDescendantsOfKind(S.CallExpression)) if (t.getExpression().getText() === "defineWorkflow") return !0;
|
|
4584
4585
|
return !1;
|
|
4585
|
-
},
|
|
4586
|
+
}, Cr = T({
|
|
4586
4587
|
version: "0.34.0",
|
|
4587
4588
|
id: "0.34.0/20_workflow-authority-resolves-at-resume",
|
|
4588
4589
|
title: "A resumed workflow re-resolves its caller's authority instead of restoring it",
|
|
4589
4590
|
kind: "manual",
|
|
4590
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4591
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(Sr),
|
|
4591
4592
|
note: [
|
|
4592
4593
|
"A WORKFLOW NO LONGER CARRIES ITS CALLER'S PERMISSIONS FORWARD. IT CARRIES",
|
|
4593
4594
|
"THEIR IDENTITY, AND ASKS YOUR APP WHAT THEY MAY DO WHEN IT RUNS.",
|
|
@@ -4652,7 +4653,7 @@ var x = (e, t, n) => {
|
|
|
4652
4653
|
" contain scopes and are stripped on READ — a resumed run cannot re-assert",
|
|
4653
4654
|
" authority the old build persisted."
|
|
4654
4655
|
].join("\n")
|
|
4655
|
-
}),
|
|
4656
|
+
}), wr = /* @__PURE__ */ new Set([
|
|
4656
4657
|
"listTag",
|
|
4657
4658
|
"createTag",
|
|
4658
4659
|
"updateTag",
|
|
@@ -4660,21 +4661,21 @@ var x = (e, t, n) => {
|
|
|
4660
4661
|
"createScope",
|
|
4661
4662
|
"writeScope",
|
|
4662
4663
|
"deleteScope"
|
|
4663
|
-
]),
|
|
4664
|
+
]), Tr = (e) => {
|
|
4664
4665
|
let t = e.getFullText();
|
|
4665
4666
|
if (!/EntityAdminSpec|deriveEntityAdmins|Tag\b|Scope\b/.test(t)) return !1;
|
|
4666
4667
|
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) {
|
|
4667
4668
|
let t = e.getName();
|
|
4668
4669
|
if (t === "EntityAdminSpec" || t === "deriveEntityAdmins") return !0;
|
|
4669
4670
|
}
|
|
4670
|
-
for (let t of e.getDescendantsOfKind(
|
|
4671
|
+
for (let t of e.getDescendantsOfKind(S.PropertyAccessExpression)) if (wr.has(t.getName())) return !0;
|
|
4671
4672
|
return !1;
|
|
4672
|
-
},
|
|
4673
|
+
}, Er = T({
|
|
4673
4674
|
version: "0.34.0",
|
|
4674
4675
|
id: "0.34.0/21_admin-gates-on-declared-access",
|
|
4675
4676
|
title: "The derived admin gates on the api's DECLARED access — the invented `<table>:create` scopes are gone",
|
|
4676
4677
|
kind: "manual",
|
|
4677
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4678
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(Tr),
|
|
4678
4679
|
note: [
|
|
4679
4680
|
"YOUR ADMIN WAS GATING ON SCOPE STRINGS NOTHING PRODUCED.",
|
|
4680
4681
|
"",
|
|
@@ -4764,14 +4765,14 @@ var x = (e, t, n) => {
|
|
|
4764
4765
|
"from \"undeclared\", so an intentionally public action rendered as forbidden",
|
|
4765
4766
|
"to everybody."
|
|
4766
4767
|
].join("\n")
|
|
4767
|
-
}),
|
|
4768
|
+
}), Dr = /\b(useMutation|useAction|useWorkflow|useWorkflowSignal|useWorkflowUpdate|useUpload)\b/, Or = /FiberFailure|Cause\.squash|squashWith/, kr = T({
|
|
4768
4769
|
version: "0.34.0",
|
|
4769
4770
|
id: "0.34.0/22_write-rejections-carry-the-typed-error",
|
|
4770
4771
|
title: "A rejected mutate/run/signal/upload carries the typed error, not a FiberFailure",
|
|
4771
4772
|
kind: "manual",
|
|
4772
4773
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4773
4774
|
let t = e.getFullText();
|
|
4774
|
-
return
|
|
4775
|
+
return Dr.test(t) || Or.test(t);
|
|
4775
4776
|
}),
|
|
4776
4777
|
note: [
|
|
4777
4778
|
"WHAT CHANGED. A write hook that rejects now rejects with the typed error",
|
|
@@ -4824,12 +4825,12 @@ var x = (e, t, n) => {
|
|
|
4824
4825
|
"contract, reject from a real rpc client, or assert on the value your",
|
|
4825
4826
|
"component branches on rather than on the literal the test wrote."
|
|
4826
4827
|
].join("\n")
|
|
4827
|
-
}),
|
|
4828
|
+
}), Ar = /@voltro\/plugin-sso-saml|samlSsoPlugin/, jr = T({
|
|
4828
4829
|
version: "0.34.0",
|
|
4829
4830
|
id: "0.34.0/23_saml-signature-and-replay-defaults",
|
|
4830
4831
|
title: "SAML: response-signature requirement relaxed + surfaced; replay protection ON by default",
|
|
4831
4832
|
kind: "manual",
|
|
4832
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4833
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ar.test(e.getFullText())),
|
|
4833
4834
|
note: [
|
|
4834
4835
|
"Two defaults in `samlSsoPlugin` moved. Read both — they interact.",
|
|
4835
4836
|
"",
|
|
@@ -4920,58 +4921,58 @@ var x = (e, t, n) => {
|
|
|
4920
4921
|
"request, and the envelope is unauthenticated. If you must run unsolicited,",
|
|
4921
4922
|
"set `wantAuthnResponseSigned: true`."
|
|
4922
4923
|
].join("\n")
|
|
4923
|
-
}),
|
|
4924
|
+
}), Mr = /\b(signPayload|verifySignature)\s*\(/, Nr = T({
|
|
4924
4925
|
version: "0.34.0",
|
|
4925
4926
|
id: "0.34.0/24_webhook-signing-renders-headers",
|
|
4926
4927
|
title: "Webhook signing: `signPayload`/`verifySignature` → `signRequest`/`verifyRequest`",
|
|
4927
4928
|
kind: "manual",
|
|
4928
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4929
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Mr.test(e.getFullText())),
|
|
4929
4930
|
note: "A webhook signature scheme now renders a header MAP rather than one header,\nand verifies against the request's whole header map.\n\n - signPayload(scheme, rawBody, secret)\n → signRequest(scheme, { rawBody, secret, messageId })\n returns { headers } instead of { headerName, headerValue }\n\n - verifySignature(scheme, rawBody, secret, headerValue)\n → verifyRequest(scheme, { rawBody, secret, headers })\n\nRewrite each call site:\n\n // before\n const { headerName, headerValue } = signPayload(scheme, body, secret)\n await fetch(url, { headers: { [headerName]: headerValue }, body })\n\n // after\n const { headers } = signRequest(scheme, { rawBody: body, secret, messageId: deliveryId })\n await fetch(url, { headers, body })\n\nWHY `messageId` IS REQUIRED, AND WHY THIS IS NOT AUTOMATED:\n\n Standard Webhooks signs over `<messageId>.<timestamp>.<body>` and sends the\n id as `webhook-id` — which is also the value the spec tells consumers to\n use as their IDEMPOTENCY KEY. Pass the id that identifies the message to\n THAT receiver, stable across retries (the framework passes its delivery\n id). A fresh `randomUUID()` per attempt compiles and reads fine, and\n quietly makes every retry look like a new message to the receiver.\n\nTHE DEFAULT OUTGOING SCHEME CHANGED:\n\n `defaultOutgoingSignature()` now returns Standard Webhooks v1.0.0 —\n `webhook-id` / `webhook-timestamp` / `webhook-signature: v1,<base64>` — so\n a NEW subscription signs to an interoperable spec every conformant\n consumer library already verifies. Existing target rows are untouched:\n each stores the scheme it was created with.\n\n Its key serialization differs too: `whsec_` + base64 of 24..64 random\n bytes, HMAC-ing the DECODED bytes. `subscribe()` mints the right shape\n automatically; passing your own hex secret for a standard-webhooks target\n is REFUSED at subscribe rather than producing signatures no consumer\n accepts.\n\n To keep the previous house format on a new subscription, pass it\n explicitly: `webhooks.subscribe({ …, signing: genericHmacSignature() })`\n — the same shape as before, under a name that says it is a choice.\n\nALSO IN THIS RELEASE, no code change needed:\n\n - a 3xx response is a failure and redirects are no longer FOLLOWED (they\n were, by fetch's default — which also walked past the SSRF guard);\n - a `410 Gone` disables the target immediately, whatever the\n consecutive-failure threshold says;\n - each attempt has a 30s wire timeout (`VOLTRO_WEBHOOK_TIMEOUT_MS`, or\n `timeoutMs`). There was none, so a receiver that accepted the connection\n and never answered held the delivery workflow open indefinitely.\n\nAnd `slackSignature()` is now a REAL verifier. It shipped as a stub that\nreturned false unconditionally, workable only through a header-packing\nconvention in the route mounter. A scheme now receives the whole header map\nand reads `x-slack-request-timestamp` itself."
|
|
4930
|
-
}),
|
|
4931
|
+
}), Pr = /from\s+['"]@voltro\/plugin-presence(?:\/[a-z]+)?['"]/, Fr = /\b(lastSeen|presenceTable|memoryPresenceStore|isOnline|filterOnline|staleKeys|PresenceStore|PresenceEntry|PresenceTracker)\b/, Ir = /_voltro_presence/, Lr = T({
|
|
4931
4932
|
version: "0.34.0",
|
|
4932
4933
|
id: "0.34.0/25_presence-has-no-table",
|
|
4933
4934
|
title: "Presence: `_voltro_presence` is gone, and so is `lastSeen` on the roster",
|
|
4934
4935
|
kind: "manual",
|
|
4935
4936
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4936
4937
|
let t = e.getFullText();
|
|
4937
|
-
return
|
|
4938
|
+
return Ir.test(t) || Pr.test(t) && Fr.test(t);
|
|
4938
4939
|
}),
|
|
4939
4940
|
note: "@voltro/plugin-presence no longer declares a table. Four things moved.\n\n1. A roster member is now { key, meta } — there is NO `lastSeen`.\n\n It was the OWNING replica's clock, so on more than one replica \"active 3\n minutes ago\" rendered from it was wrong by the skew between two pods, with\n nothing to say so. It was also the only field a plain heartbeat changed,\n which is what made the roster push on every beat instead of on every change.\n\n // before\n members.map((m) => `${m.key} — ${ago(m.lastSeen)}`)\n // after: put anything time-shaped you need into meta\n usePresence(channel, { meta: { joinedAt: Date.now() } })\n members.map((m) => `${m.key} — ${ago(m.meta?.joinedAt)}`)\n\n2. `PresenceTracker.track()` and `.untrack()` return { delta, changed }.\n\n // before\n bus.announce(tracker.track({ channel, tenantId, key }))\n // after — announce ALWAYS (a peer's sweep needs the liveness signal),\n // push only on a change\n const write = tracker.track({ channel, tenantId, key })\n bus.announce(write.delta)\n if (write.changed) publishReactivity(store, presenceRoster)\n\n Do not blind-append `.delta` and move on: dropping `changed` restores the\n per-heartbeat push this release removed, and nothing will report it.\n\n `attachPresenceBus` also takes an optional `onRosterChanged` now — call it\n if you drive the bus yourself, or a peer's join and a peer's death will\n update your tracker and push nobody.\n\n3. These exports are DELETED, with no replacement:\n\n presenceTable, PresenceStore, PresenceEntry,\n memoryPresenceStore, isOnline, filterOnline, staleKeys\n\n They were the table era's read model — an abstraction over rows plus the\n staleness filter a row needed because it outlived the client that wrote it.\n Presence has neither. Reading the roster is `presence.list` / `usePresence`;\n the staleness sweep is the plugin's own and needs nothing from you.\n\n4. If your `source:` names '_voltro_presence' as a string, point it at the\n channel object instead:\n\n import { presenceRoster } from '@voltro/plugin-presence'\n defineQuery({ name: 'room.summary', source: presenceRoster, ... })\n\n And to push it yourself:\n\n import { publishReactivity } from '@voltro/protocol'\n publishReactivity(ctx.store, presenceRoster)\n\nTHE TABLE IS NOT DROPPED FOR YOU. The declarative differ never plans a drop for\na framework table no app declares — deliberately, so a live `_voltro_*` table is\nnever destroyed by an app that simply does not know about it. So an existing\n`_voltro_presence` stays, empty, until you remove it:\n\n DROP TABLE IF EXISTS _voltro_presence;\n\nIt holds no data anyone can use: nothing has written a row to it since presence\nmoved into memory."
|
|
4940
|
-
}),
|
|
4941
|
+
}), Rr = (e) => {
|
|
4941
4942
|
let t = e.getArguments()[0];
|
|
4942
|
-
if (t === void 0 || !t.isKind(
|
|
4943
|
+
if (t === void 0 || !t.isKind(S.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
|
|
4943
4944
|
let n = /* @__PURE__ */ new Set();
|
|
4944
4945
|
for (let e of t.getProperties()) {
|
|
4945
|
-
let t = (e.asKind(
|
|
4946
|
+
let t = (e.asKind(S.PropertyAssignment) ?? e.asKind(S.ShorthandPropertyAssignment))?.getName?.();
|
|
4946
4947
|
t !== void 0 && n.add(t);
|
|
4947
4948
|
}
|
|
4948
4949
|
return n;
|
|
4949
|
-
},
|
|
4950
|
+
}, zr = (e, t) => /defineEvent\s*\(/.test(e) ? t.some((e) => {
|
|
4950
4951
|
if (e.getExpression().getText() !== "defineEvent") return !1;
|
|
4951
|
-
let t =
|
|
4952
|
+
let t = Rr(e);
|
|
4952
4953
|
return !t.has("guards") && !t.has("openAccess");
|
|
4953
|
-
}) : !1,
|
|
4954
|
+
}) : !1, Br = T({
|
|
4954
4955
|
version: "0.35.0",
|
|
4955
4956
|
id: "0.35.0/01_event-access-decision",
|
|
4956
4957
|
title: "Every declared event decides who may listen (`guards:` or `openAccess:`)",
|
|
4957
4958
|
kind: "manual",
|
|
4958
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4959
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => zr(e.getFullText(), e.getDescendantsOfKind(S.CallExpression))),
|
|
4959
4960
|
note: (/* @__PURE__ */ "YOUR APP WILL NOT BOOT UNTIL EVERY DECLARED EVENT DECIDES WHO MAY LISTEN.(This closes the sibling of 0.34.0's procedure gate: `defineEvent`'s `guards:`(was optional and enforcement skipped an empty list, so an event with no(decision was silently subscribable by ANY authenticated session that could(open the socket.((WHAT TO DO, IN THIS ORDER:((1) LIST THEM. `voltro doctor` prints every undecided event with its tag and( file — the same set, from the same function, that the boot refuses on.( `voltro doctor --json` carries it as `accessDecisions.undecided` for CI.((2) DECIDE EACH ONE. There are exactly two answers and they are not the same( claim:(( guards: [{ scope: 'display:read' }] a subscriber must hold a scope( openAccess: 'public scoreboard' anyone may listen — and why(( `openAccess` takes a REASON, not a boolean. It is what a reviewer reads( later, and it is what makes \"we decided this is open\" distinguishable( from \"nobody looked\".((3) DO NOT RUBBER-STAMP. Reaching for a scope every caller already holds( satisfies the gate, reads as protection, and enforces nothing. If the( event is open, say `openAccess`. If it should not be open, this gate just( found a real hole — an event often carries exactly the data its table( guards behind a scoped query.((IF YOU NEED TO SHIP FIRST. The same field that covers procedures covers(events, in `app.config.ts`:(( export default { security: { defaultDeny: false } }((That restores the old default-allow for the WHOLE app, in one place a(reviewer can see. There is no env var for it, on purpose. `voltro doctor`(keeps listing the undecided events while it is off, marked advisory.((NOT AFFECTED: apps already running `security: { defaultDeny: false }`, and(events your PLUGINS declare — the gate reads your app's own discovered(`*.event.ts` files only.".split("(")).join("\n")
|
|
4960
|
-
}),
|
|
4961
|
+
}), Vr = /\brouteHttp\s*\(/, Hr = T({
|
|
4961
4962
|
version: "0.34.0",
|
|
4962
4963
|
id: "0.34.0/26_route-http-is-async",
|
|
4963
4964
|
title: "`routeHttp` returns a Promise",
|
|
4964
4965
|
kind: "manual",
|
|
4965
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4966
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Vr.test(e.getFullText())),
|
|
4966
4967
|
note: "`routeHttp` (@voltro/mcp) is async now:\n\n routeHttp(options): HttpOutcome // before\n routeHttp(options): Promise<HttpOutcome> // after\n\nAwait it, and make the enclosing function async if it is not already:\n\n // before\n const outcome = routeHttp({ method, url, body, tools })\n if (outcome.kind === 'json') respond(outcome)\n\n // after\n const outcome = await routeHttp({ method, url, body, tools })\n if (outcome.kind === 'json') respond(outcome)\n\nWhy it moved: `tools/list` now folds in the app's agent tools and `tools/call`\nexecutes one, so two of the methods reach the app. The rest still resolve without\ntouching anything, and the function is still pure in the sense that matters — it\nbinds no socket, and a test supplies a `live` double.\n\nIf you use the shipped `serveHttp` transport there is nothing to do; it already\nawaits."
|
|
4967
|
-
}),
|
|
4968
|
+
}), Ur = /@voltro\/plugin-(billing|governance|storage)/, Wr = /* @__PURE__ */ RegExp("\\b(billing\\.(startCheckout|portalUrl|previewChange|changePlan|changeSeats|invoices|reportUsage)|governance\\.(export|erase)|storage\\.(mintUploadUrl|ingestUrl|listRefs))\\b"), Gr = /\bdefinePluginRoute\s*\(|\broutes\s*:\s*\[/, Kr = T({
|
|
4968
4969
|
version: "0.35.0",
|
|
4969
4970
|
id: "0.35.0/02_plugin-procedures-carry-access-decisions",
|
|
4970
4971
|
title: "First-party plugin procedures carry access decisions; twelve now require a scope",
|
|
4971
4972
|
kind: "manual",
|
|
4972
4973
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4973
4974
|
let t = e.getFullText();
|
|
4974
|
-
return
|
|
4975
|
+
return Ur.test(t) || Wr.test(t) || Gr.test(t) && /VoltroPlugin|definePlugin/.test(t);
|
|
4975
4976
|
}),
|
|
4976
4977
|
note: [
|
|
4977
4978
|
"Every first-party plugin rpc route now declares an access decision, and the",
|
|
@@ -5036,12 +5037,12 @@ var x = (e, t, n) => {
|
|
|
5036
5037
|
"`openAccess` with the reason each is safe (self-scoped rows, signed",
|
|
5037
5038
|
"subject-bound tickets, per-object grants), and behave exactly as before."
|
|
5038
5039
|
].join("\n")
|
|
5039
|
-
}),
|
|
5040
|
+
}), qr = /AnalyticsMirrorImpl/, Jr = T({
|
|
5040
5041
|
version: "0.35.0",
|
|
5041
5042
|
id: "0.35.0/03_analytics-mirror-max-version",
|
|
5042
5043
|
title: "Custom analytics mirror sinks answer `maxVersion` (fleet-scope seed read)",
|
|
5043
5044
|
kind: "manual",
|
|
5044
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5045
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => qr.test(e.getFullText())),
|
|
5045
5046
|
note: [
|
|
5046
5047
|
"The analytics CDC-mirror now derives its version from the CHANGE under",
|
|
5047
5048
|
"`changeScope: 'fleet'` (postgres CDC, mysql binlog): warehouse baseline +",
|
|
@@ -5066,44 +5067,44 @@ var x = (e, t, n) => {
|
|
|
5066
5067
|
"",
|
|
5067
5068
|
"The read is issued at most once per key per process, off the commit path."
|
|
5068
5069
|
].join("\n")
|
|
5069
|
-
}),
|
|
5070
|
+
}), Yr = (e) => /RunStepStatus/.test(e) || /['"]succeeded['"]/.test(e) && /['"]waiting['"]/.test(e) && /RunStep|runStep|flow/i.test(e), Xr = T({
|
|
5070
5071
|
version: "0.35.0",
|
|
5071
5072
|
id: "0.35.0/04_flow-run-step-skipped",
|
|
5072
5073
|
title: "A flow run step can now be 'skipped' — handle it where you render a run timeline",
|
|
5073
5074
|
kind: "manual",
|
|
5074
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5075
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Yr(e.getFullText())),
|
|
5075
5076
|
note: (/* @__PURE__ */ "A FLOW RUN STEP CAN NOW BE 'skipped'.((`when:` on a flow step gates it on a prior value, and a step whose condition(is false is SKIPPED rather than run. That is a sixth member of(`RunStepStatus`, so an exhaustive switch over it no longer compiles, and a(lookup keyed by status now has a hole.((WHAT TO DO:((1) FIND IT. Anywhere you render or branch on a run step status — a timeline( component, a status badge map, a progress calculation.((2) DECIDE WHAT A SKIPPED STEP LOOKS LIKE. It is NOT a failure and NOT a( success: it did not run, on purpose, and it produced no output. The( step carries `skipReason` — the rendered condition, e.g.( `{{mode}} equals \"full\"` — so the timeline can say WHY.((3) DO NOT DEFAULT IT TO INVISIBLE. Dropping the row compiles and reads fine( in review, and it reproduces exactly the problem `skipReason` exists to( prevent: a step that vanished is indistinguishable from a step nobody( declared, which is the first thing a user reports as a bug.((A PROGRESS COUNT probably needs a second look too: `succeeded / total` now(under-reports a completed run that skipped a step. Counting(`succeeded + skipped` against the total is usually what was meant.((If your app does not render a flow timeline, there is nothing to do — this(note fires on any file that names the status type or its literals.".split("(")).join("\n")
|
|
5076
|
-
}),
|
|
5077
|
+
}), Zr = T({
|
|
5077
5078
|
version: "0.35.0",
|
|
5078
5079
|
id: "0.35.0/05_declared-schema-drops-runtime-flags",
|
|
5079
5080
|
title: "The declared schema no longer reads `CDC` / `VOLTRO_UNDO` / `VOLTRO_TRACING_PERSIST`",
|
|
5080
5081
|
kind: "manual",
|
|
5081
5082
|
appliesTo: () => !0,
|
|
5082
5083
|
note: (/* @__PURE__ */ "The set of framework tables your app DECLARES is hashed into the schema(fingerprint. A pre-deploy `voltro db apply` records that hash; every serving(container recomputes it and refuses to boot if it differs. Until this release(three RUNTIME flags fed into that set:(( CDC=0 removed `_voltro_cdc_offsets`( VOLTRO_UNDO=on added `_voltro_undo_log`( VOLTRO_TRACING_PERSIST=all added `_voltro_traces`((All three read as pure runtime knobs, so they tend to live on the PODS and(not on the pre-upgrade migrate Job — which is a green migrate followed by(every pod in CrashLoopBackOff. This was reported against a Helm deployment(where `CDC: \"0\"` was in all three overlays' pod blocks and none of their(jobs; it had been latent for months and would have fired on the next deploy.((WHAT YOU MUST DO — only if you set one of these to turn a table ON in(production:(( * `VOLTRO_UNDO=on` in production → add to app.config.ts:( export default { schema: { undo: true }, … }( * `VOLTRO_TRACING_PERSIST` keeping trace history in production → add:( export default { schema: { traces: true }, … }(( Without the declaration, `voltro serve` now REFUSES to boot and names the( field. That refusal is the point: before, the same configuration either( wrote to a table nobody created or produced a fingerprint the migrate job( could not reproduce.(( Turning capture OFF still needs no declaration and never will — a declared( table nobody writes to is harmless. Only turning it ON is a schema claim.((WHAT HAPPENS WITHOUT ANY ACTION:(( * On mariadb or mssql you will see ONE additive `create-table` in your next( plan: `_voltro_cdc_offsets` is now declared by the DIALECT rather than by( `CDC`, so it exists whether or not that process drives CDC. It is an( offsets table — a handful of rows — and the alternative was leaving a( runtime flag able to split your fleet.( * On postgres/sqlite nothing changes: those dialects have no offsets table.((AND ONE THING WORTH DOING ANYWAY: if you are upgrading a deployment that has(a separate migrate job, diff that job's `env:` against your pods' once. The(framework can now guarantee its OWN tables do not depend on a runtime flag;(it cannot make the same guarantee for a plugin's `extendSchema.tables`, which(is app code and may read anything. A `prod-mismatch` refusal now prints which(tables this process decided and from which input, so the comparison is quick.".split("(")).join("\n")
|
|
5083
|
-
}),
|
|
5084
|
+
}), W = "session_expired", Qr = T({
|
|
5084
5085
|
version: "0.35.1",
|
|
5085
5086
|
id: "0.35.1/01_unauthorized-replaces-session-expired",
|
|
5086
5087
|
title: "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
|
|
5087
5088
|
kind: "transform",
|
|
5088
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(
|
|
5089
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(W)),
|
|
5089
5090
|
transform: ({ project: e, log: t }) => {
|
|
5090
5091
|
let n = [];
|
|
5091
5092
|
for (let t of e.getSourceFiles()) {
|
|
5092
|
-
if (!t.getFullText().includes(
|
|
5093
|
+
if (!t.getFullText().includes(W)) continue;
|
|
5093
5094
|
let e = !1;
|
|
5094
|
-
for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() ===
|
|
5095
|
+
for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === W && (n.setLiteralValue("unauthorized"), e = !0);
|
|
5095
5096
|
e && n.push(t.getFilePath());
|
|
5096
5097
|
}
|
|
5097
|
-
n.length !== 0 && t(`rewrote '${
|
|
5098
|
+
n.length !== 0 && t(`rewrote '${W}' to 'unauthorized' in ${n.length} file(s).\n A 401 now means only "the credential was refused" — expired, revoked, wrong\n scope and MALFORMED all produce that status, and the old name asserted the\n first one. If a rewritten branch was meant to catch a credential your\n CONNECTION VAULT could not produce (no grant, revoked grant, failed refresh),\n change it to 'credential_unusable' — that is the code the vault raises, and it\n is the one where re-auth is genuinely the right action.\n A health check that logs the user out on this code should read 'unauthorized'\n as "ask again", not as "the session is gone".\n` + n.map((e) => ` ${e}`).join("\n"));
|
|
5098
5099
|
}
|
|
5099
|
-
}),
|
|
5100
|
+
}), $r = /\(\s*\{[^}]*\.\.\./, ei = /\bquery\??\.?\(\s*['"`]/, ti = T({
|
|
5100
5101
|
version: "0.37.0",
|
|
5101
5102
|
id: "0.37.0/01_procedure-input-rejects-undeclared-fields",
|
|
5102
5103
|
title: "a procedure input rejects a field it does not declare (it used to drop it)",
|
|
5103
5104
|
kind: "manual",
|
|
5104
5105
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5105
5106
|
let t = e.getFullText();
|
|
5106
|
-
return
|
|
5107
|
+
return $r.test(t) || ei.test(t) || E("query").test(t);
|
|
5107
5108
|
}),
|
|
5108
5109
|
note: [
|
|
5109
5110
|
"A procedure call whose input carries a field the schema does not declare",
|
|
@@ -5165,33 +5166,33 @@ var x = (e, t, n) => {
|
|
|
5165
5166
|
"two, the only safe direction is to refuse — and a warning would have to be",
|
|
5166
5167
|
"read by someone, in a log, after the wrong rows were already served."
|
|
5167
5168
|
].join("\n")
|
|
5168
|
-
}),
|
|
5169
|
+
}), G = "@voltro/plugin-auth", K = "@voltro/protocol", q = "IMPERSONATION_METADATA_KEY", ni = T({
|
|
5169
5170
|
version: "0.38.0",
|
|
5170
5171
|
id: "0.38.0/01_impersonation-key-in-protocol",
|
|
5171
|
-
title: `\`${
|
|
5172
|
+
title: `\`${q}\` is imported from @voltro/protocol`,
|
|
5172
5173
|
kind: "transform",
|
|
5173
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() ===
|
|
5174
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === G && e.getNamedImports().some((e) => e.getName() === q))),
|
|
5174
5175
|
transform: ({ project: e, log: t }) => {
|
|
5175
5176
|
let n = 0;
|
|
5176
5177
|
for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
|
|
5177
|
-
if (e.getModuleSpecifierValue() !==
|
|
5178
|
-
let r = e.getNamedImports().find((e) => e.getName() ===
|
|
5178
|
+
if (e.getModuleSpecifierValue() !== G) continue;
|
|
5179
|
+
let r = e.getNamedImports().find((e) => e.getName() === q);
|
|
5179
5180
|
if (r === void 0) continue;
|
|
5180
5181
|
let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
|
|
5181
5182
|
r.remove();
|
|
5182
|
-
let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() ===
|
|
5183
|
-
name:
|
|
5183
|
+
let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === K && e.isTypeOnly() === a), s = {
|
|
5184
|
+
name: q,
|
|
5184
5185
|
...i === void 0 ? {} : { alias: i }
|
|
5185
5186
|
};
|
|
5186
5187
|
o === void 0 ? t.addImportDeclaration({
|
|
5187
|
-
moduleSpecifier:
|
|
5188
|
+
moduleSpecifier: K,
|
|
5188
5189
|
isTypeOnly: a,
|
|
5189
5190
|
namedImports: [s]
|
|
5190
5191
|
}) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
|
|
5191
5192
|
}
|
|
5192
|
-
n > 0 && t(`${
|
|
5193
|
+
n > 0 && t(`${q}: ${n} import(s) repointed to ${K}`);
|
|
5193
5194
|
}
|
|
5194
|
-
}),
|
|
5195
|
+
}), ri = T({
|
|
5195
5196
|
version: "0.38.0",
|
|
5196
5197
|
id: "0.38.0/02_startup-failure-refuses-boot",
|
|
5197
5198
|
title: "a `*.startup.ts` that fails refuses the boot (it used to warn and continue)",
|
|
@@ -5276,12 +5277,12 @@ var x = (e, t, n) => {
|
|
|
5276
5277
|
"long work after, so a failure in the long half cannot hide a missing",
|
|
5277
5278
|
"registration."
|
|
5278
5279
|
].join("\n")
|
|
5279
|
-
}),
|
|
5280
|
+
}), ii = /\bStruct\(\s*\{\s*\}\s*\)/, ai = T({
|
|
5280
5281
|
version: "0.39.0",
|
|
5281
5282
|
id: "0.39.0/01_empty-input-schema-rejects-every-field",
|
|
5282
5283
|
title: "a procedure declaring `Schema.Struct({})` used to accept every field and now accepts none",
|
|
5283
5284
|
kind: "manual",
|
|
5284
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5285
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ii.test(e.getFullText())),
|
|
5285
5286
|
note: [
|
|
5286
5287
|
"CHECK YOUR `Schema.Struct({})` PROCEDURES FIRST. They have been accepting",
|
|
5287
5288
|
"every field anyone sent them, and they now accept none — the one jump in",
|
|
@@ -5335,14 +5336,14 @@ var x = (e, t, n) => {
|
|
|
5335
5336
|
"A procedure that genuinely takes no input keeps `Schema.Struct({})` and is",
|
|
5336
5337
|
"correct as it stands — callers must send `{}` or nothing."
|
|
5337
5338
|
].join("\n")
|
|
5338
|
-
}),
|
|
5339
|
+
}), oi = T({
|
|
5339
5340
|
version: "0.40.0",
|
|
5340
5341
|
id: "0.40.0/01_cold-start-failure-is-its-own-state",
|
|
5341
5342
|
title: "a failed cold start is `failed: true`, not `loading: true`",
|
|
5342
5343
|
kind: "manual",
|
|
5343
5344
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5344
5345
|
let t = e.getFullText();
|
|
5345
|
-
return
|
|
5346
|
+
return E("useSubscription").test(t) || E("usePreloadedSubscription").test(t) || /\buseWorkflow\w*\(/.test(t);
|
|
5346
5347
|
}),
|
|
5347
5348
|
note: [
|
|
5348
5349
|
"A subscription whose COLD START failed no longer reports `loading: true`.",
|
|
@@ -5400,14 +5401,14 @@ var x = (e, t, n) => {
|
|
|
5400
5401
|
" reconnect discards the error and re-subscribes, so the entry returns to",
|
|
5401
5402
|
" pending on its own."
|
|
5402
5403
|
].join("\n")
|
|
5403
|
-
}),
|
|
5404
|
+
}), si = T({
|
|
5404
5405
|
version: "0.40.0",
|
|
5405
5406
|
id: "0.40.0/02_rejected-credential-widens-guard-results",
|
|
5406
5407
|
title: "a rejected credential answers `Unauthenticated`, widening three guard results",
|
|
5407
5408
|
kind: "manual",
|
|
5408
5409
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5409
5410
|
let t = e.getFullText();
|
|
5410
|
-
return
|
|
5411
|
+
return E("checkGuards").test(t) || E("checkGuardsEffect").test(t) || E("bindEvent").test(t);
|
|
5411
5412
|
}),
|
|
5412
5413
|
note: [
|
|
5413
5414
|
"A guard refusing a caller whose CREDENTIAL was rejected now answers",
|
|
@@ -5436,16 +5437,16 @@ var x = (e, t, n) => {
|
|
|
5436
5437
|
"`openAccess` procedures are untouched: the stamp only travels, and only a",
|
|
5437
5438
|
"guard that actually refuses ever spends it."
|
|
5438
5439
|
].join("\n")
|
|
5439
|
-
}),
|
|
5440
|
+
}), ci = "@voltro/web/middleware", li = /(^|[\\/])middleware\.tsx?$/, ui = T({
|
|
5440
5441
|
version: "0.41.0",
|
|
5441
5442
|
id: "0.41.0/01_middleware-defines-where-it-runs",
|
|
5442
5443
|
title: "`middleware.ts` exports `defineMiddleware(...)` and can say where it runs",
|
|
5443
5444
|
kind: "transform",
|
|
5444
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5445
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => li.test(e.getFilePath()) ? e.getExportAssignment((e) => !e.isExportEquals()) !== void 0 || e.getFunctions().some((e) => e.isDefaultExport()) : !1),
|
|
5445
5446
|
transform: ({ project: e, log: t }) => {
|
|
5446
5447
|
let n = 0;
|
|
5447
5448
|
for (let t of e.getSourceFiles()) {
|
|
5448
|
-
if (!
|
|
5449
|
+
if (!li.test(t.getFilePath()) || t.getFullText().includes("defineMiddleware")) continue;
|
|
5449
5450
|
let e = t.getExportAssignment((e) => !e.isExportEquals());
|
|
5450
5451
|
if (e !== void 0) {
|
|
5451
5452
|
let t = e.getExpression().getText();
|
|
@@ -5456,9 +5457,9 @@ var x = (e, t, n) => {
|
|
|
5456
5457
|
let r = e.getText().replace(/^\s*export\s+default\s+/, "");
|
|
5457
5458
|
e.replaceWithText(`export default defineMiddleware({ run: ${r} })`), n += 1;
|
|
5458
5459
|
}
|
|
5459
|
-
let r = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() ===
|
|
5460
|
+
let r = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === ci);
|
|
5460
5461
|
r === void 0 ? t.insertImportDeclaration(0, {
|
|
5461
|
-
moduleSpecifier:
|
|
5462
|
+
moduleSpecifier: ci,
|
|
5462
5463
|
namedImports: [{ name: "defineMiddleware" }]
|
|
5463
5464
|
}) : r.getNamedImports().some((e) => e.getName() === "defineMiddleware") || r.addNamedImport({ name: "defineMiddleware" });
|
|
5464
5465
|
}
|
|
@@ -5495,7 +5496,7 @@ var x = (e, t, n) => {
|
|
|
5495
5496
|
"Non-page requests (assets, unknown paths) are NOT matched unless you ask with",
|
|
5496
5497
|
"`assets: true`. There is no render there, so only `setCookies` takes effect."
|
|
5497
5498
|
].join("\n")
|
|
5498
|
-
}),
|
|
5499
|
+
}), di = T({
|
|
5499
5500
|
version: "0.43.0",
|
|
5500
5501
|
id: "0.43.0/01_dangling-reference-to-rows-refused",
|
|
5501
5502
|
title: "`DanglingReferenceError` → `RowsRefusedError` (named for the outcome, not a guessed cause)",
|
|
@@ -5509,7 +5510,7 @@ var x = (e, t, n) => {
|
|
|
5509
5510
|
});
|
|
5510
5511
|
return r > 0 && n(`renamed ${r} import(s) of DanglingReferenceError → RowsRefusedError. A tag STRING (Effect.catchTag('DanglingReferenceError', …) or err._tag === '…') is NOT rewritten — change it to 'RowsRefusedError'. The payload also gained primaryCount, and each row a boolean derived.`), r;
|
|
5511
5512
|
}
|
|
5512
|
-
}),
|
|
5513
|
+
}), fi = T({
|
|
5513
5514
|
version: "0.44.0",
|
|
5514
5515
|
id: "0.44.0/01_import-outcome",
|
|
5515
5516
|
title: "`runImport` returns an ImportOutcome — read `.manifest` for the bundle",
|
|
@@ -5530,7 +5531,7 @@ var x = (e, t, n) => {
|
|
|
5530
5531
|
"emptied of its old rows and holding part of the new ones. If you relied on per-table resume for a replace,",
|
|
5531
5532
|
"that is the non-atomic path."
|
|
5532
5533
|
].join("\n")
|
|
5533
|
-
}),
|
|
5534
|
+
}), pi = T({
|
|
5534
5535
|
version: "0.45.0",
|
|
5535
5536
|
id: "0.45.0/01_typed-source-table-name",
|
|
5536
5537
|
title: "`source:` is typechecked against the app’s tables, from the next `voltro dev`",
|
|
@@ -5570,19 +5571,20 @@ var x = (e, t, n) => {
|
|
|
5570
5571
|
"To opt out entirely, delete `voltro-tables.generated.d.ts` and exclude it",
|
|
5571
5572
|
"from your tsconfig; `source:` widens back to `string`."
|
|
5572
5573
|
].join("\n")
|
|
5573
|
-
}),
|
|
5574
|
+
}), mi = T({
|
|
5574
5575
|
version: "0.46.0",
|
|
5575
5576
|
id: "0.46.0/01_typed-aggregate-source",
|
|
5576
5577
|
title: "an aggregate’s `incremental.source` is typechecked against the app’s tables",
|
|
5577
5578
|
kind: "manual",
|
|
5578
5579
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => /\bincremental\s*:/.test(e.getFullText())),
|
|
5579
5580
|
note: (/* @__PURE__ */ "An aggregate that opts into incremental maintenance names the table whose(CDC deltas drive it:(( incremental: { source: 'tasks', op: 'count' }((That name is now checked against your app’s tables, the same way a query’s(`source:` has been since 0.45.0.((WHY IT MATTERS MORE HERE((A name matching no table does not error. The runner subscribes to something(nothing writes, no delta ever arrives, and the aggregate quietly stops(tracking its input — while every read of it still succeeds and still returns(a number. There is no failing request to notice.((WHEN IT STARTS APPLYING((At your next `voltro dev` or `voltro codegen`, not at this upgrade — those(write `voltro-tables.generated.d.ts`, and that file is what narrows the type.(Until it exists the field is `string`, exactly as it is today.((WHAT TO DO((Boot once and read what `tsc` flags. A flagged name is either misspelled or(renamed away — a real defect, and that aggregate has not been maintaining —(or it is computed at runtime, in which case annotate it `string`.((The boot also warns now: an aggregate whose CDC source resolves to no table(is named in the same stale-`source:` warning queries and streams appear in.".split("(")).join("\n")
|
|
5580
|
-
}),
|
|
5581
|
+
}), hi = T({
|
|
5581
5582
|
version: "0.49.0",
|
|
5582
5583
|
id: "0.49.0/01_data-imports-to-transfers",
|
|
5583
5584
|
title: "`voltro data imports` → `voltro data transfers`",
|
|
5584
5585
|
kind: "manual",
|
|
5585
|
-
|
|
5586
|
+
reach: "beyond-source",
|
|
5587
|
+
appliesTo: ({ project: e, text: t }) => t.find("data imports").length > 0 || e.getSourceFiles().some((e) => e.getFullText().includes("data imports")),
|
|
5586
5588
|
note: [
|
|
5587
5589
|
"The subcommand that reads the data-transfer history has been renamed:",
|
|
5588
5590
|
"",
|
|
@@ -5609,31 +5611,31 @@ var x = (e, t, n) => {
|
|
|
5609
5611
|
"marker, so `voltro db apply` (or a `voltro dev` boot) moves it with its rows on",
|
|
5610
5612
|
"every dialect. If you query it directly, the new name is the one to use."
|
|
5611
5613
|
].join("\n")
|
|
5612
|
-
}),
|
|
5614
|
+
}), gi = ["@voltro/database", "voltro/database"], _i = [
|
|
5613
5615
|
["ImportRun", "DataTransferRun"],
|
|
5614
5616
|
["describeImportRun", "describeTransferRun"],
|
|
5615
5617
|
["IMPORT_RUNS_TABLE", "DATA_TRANSFERS_TABLE"],
|
|
5616
5618
|
["_voltroImportRunsTable", "_voltroDataTransfersTable"]
|
|
5617
|
-
],
|
|
5619
|
+
], J = "_voltro_data_imports", vi = "_voltro_data_transfers", yi = (e) => {
|
|
5618
5620
|
let t = 0;
|
|
5619
|
-
for (let n of e.getSourceFiles()) n.getFullText().includes(
|
|
5620
|
-
if (
|
|
5621
|
+
for (let n of e.getSourceFiles()) n.getFullText().includes(J) && n.forEachDescendant((e) => {
|
|
5622
|
+
if (x.isStringLiteral(e) || x.isNoSubstitutionTemplateLiteral(e) || x.isTemplateHead(e) || x.isTemplateMiddle(e) || x.isTemplateTail(e) || x.isRegularExpressionLiteral(e)) {
|
|
5621
5623
|
let n = e.getText();
|
|
5622
|
-
if (!n.includes(
|
|
5623
|
-
e.replaceWithText(n.split(
|
|
5624
|
+
if (!n.includes(J)) return;
|
|
5625
|
+
e.replaceWithText(n.split(J).join(vi)), t++;
|
|
5624
5626
|
}
|
|
5625
5627
|
});
|
|
5626
5628
|
return t;
|
|
5627
|
-
},
|
|
5629
|
+
}, bi = [
|
|
5630
|
+
he,
|
|
5628
5631
|
ge,
|
|
5629
|
-
|
|
5632
|
+
ve,
|
|
5630
5633
|
ye,
|
|
5631
5634
|
be,
|
|
5632
|
-
|
|
5633
|
-
|
|
5635
|
+
Ce,
|
|
5636
|
+
Te,
|
|
5634
5637
|
Ee,
|
|
5635
5638
|
De,
|
|
5636
|
-
Oe,
|
|
5637
5639
|
je,
|
|
5638
5640
|
Me,
|
|
5639
5641
|
Fe,
|
|
@@ -5641,6 +5643,7 @@ var x = (e, t, n) => {
|
|
|
5641
5643
|
ze,
|
|
5642
5644
|
Be,
|
|
5643
5645
|
Ue,
|
|
5646
|
+
We,
|
|
5644
5647
|
Ge,
|
|
5645
5648
|
Ke,
|
|
5646
5649
|
qe,
|
|
@@ -5653,9 +5656,8 @@ var x = (e, t, n) => {
|
|
|
5653
5656
|
et,
|
|
5654
5657
|
tt,
|
|
5655
5658
|
nt,
|
|
5656
|
-
rt,
|
|
5657
5659
|
ct,
|
|
5658
|
-
|
|
5660
|
+
rt,
|
|
5659
5661
|
st,
|
|
5660
5662
|
mt,
|
|
5661
5663
|
yt,
|
|
@@ -5677,90 +5679,226 @@ var x = (e, t, n) => {
|
|
|
5677
5679
|
Qt,
|
|
5678
5680
|
en,
|
|
5679
5681
|
an,
|
|
5682
|
+
on,
|
|
5680
5683
|
sn,
|
|
5681
5684
|
cn,
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5685
|
+
un,
|
|
5686
|
+
fn,
|
|
5687
|
+
mn,
|
|
5688
|
+
gn,
|
|
5689
|
+
vn,
|
|
5690
|
+
Cn,
|
|
5691
|
+
Tn,
|
|
5692
|
+
Dn,
|
|
5693
|
+
kn,
|
|
5694
|
+
jn,
|
|
5692
5695
|
Mn,
|
|
5693
5696
|
Nn,
|
|
5694
5697
|
Pn,
|
|
5695
5698
|
Fn,
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5699
|
+
Ln,
|
|
5700
|
+
Vn,
|
|
5701
|
+
Un,
|
|
5702
|
+
Gn,
|
|
5700
5703
|
Kn,
|
|
5701
5704
|
qn,
|
|
5702
5705
|
Jn,
|
|
5703
|
-
|
|
5706
|
+
Zn,
|
|
5707
|
+
ir,
|
|
5704
5708
|
Qn,
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
sr,
|
|
5709
|
+
nr,
|
|
5710
|
+
or,
|
|
5711
|
+
cr,
|
|
5709
5712
|
lr,
|
|
5710
5713
|
ur,
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5714
|
+
pr,
|
|
5715
|
+
hr,
|
|
5716
|
+
xr,
|
|
5717
|
+
Cr,
|
|
5718
|
+
yr,
|
|
5719
|
+
Er,
|
|
5720
|
+
kr,
|
|
5721
|
+
jr,
|
|
5722
|
+
Nr,
|
|
5723
|
+
Lr,
|
|
5724
|
+
Hr,
|
|
5725
|
+
Br,
|
|
5726
|
+
Kr,
|
|
5727
|
+
Jr,
|
|
5728
|
+
Xr,
|
|
5726
5729
|
Zr,
|
|
5727
5730
|
Qr,
|
|
5728
|
-
|
|
5731
|
+
ti,
|
|
5729
5732
|
ni,
|
|
5730
5733
|
ri,
|
|
5731
|
-
|
|
5734
|
+
ai,
|
|
5732
5735
|
oi,
|
|
5733
5736
|
si,
|
|
5734
|
-
ci,
|
|
5735
|
-
li,
|
|
5736
5737
|
ui,
|
|
5737
5738
|
di,
|
|
5738
5739
|
fi,
|
|
5739
5740
|
pi,
|
|
5740
5741
|
mi,
|
|
5741
|
-
|
|
5742
|
+
hi,
|
|
5743
|
+
T({
|
|
5742
5744
|
version: "0.49.0",
|
|
5743
5745
|
id: "0.49.0/02_import-run-symbols",
|
|
5744
5746
|
title: "`ImportRun` / `describeImportRun` / `IMPORT_RUNS_TABLE` → their `…TransferRun` names",
|
|
5745
5747
|
kind: "transform",
|
|
5746
5748
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5747
5749
|
let t = e.getFullText();
|
|
5748
|
-
return t.includes(
|
|
5750
|
+
return t.includes(J) ? !0 : gi.some((e) => t.includes(e)) ? _i.some(([e]) => t.includes(e)) : !1;
|
|
5749
5751
|
}),
|
|
5750
5752
|
transform: ({ project: e, helpers: t, log: n }) => {
|
|
5751
5753
|
let r = 0;
|
|
5752
|
-
for (let n of
|
|
5754
|
+
for (let n of gi) for (let [i, a] of _i) r += t.renameImport(e, {
|
|
5753
5755
|
module: n,
|
|
5754
5756
|
from: i,
|
|
5755
5757
|
to: a
|
|
5756
5758
|
});
|
|
5757
|
-
let i =
|
|
5759
|
+
let i = yi(e);
|
|
5758
5760
|
if (r + i === 0) return;
|
|
5759
5761
|
let a = [];
|
|
5760
|
-
r > 0 && a.push(`${r} import(s) renamed to their \`…TransferRun\` names`), i > 0 && a.push(`${i} hand-spelled \`${
|
|
5762
|
+
r > 0 && a.push(`${r} import(s) renamed to their \`…TransferRun\` names`), i > 0 && a.push(`${i} hand-spelled \`${J}\` → \`${vi}\``), n(`data-transfer run history: ${a.join("; ")}. An export writes to this record now, so the old names described half of what the table holds. The table itself carries a rename marker — \`voltro db apply\` (or a \`voltro dev\` boot) moves it with its rows on every dialect, so no data is lost and nothing is required of you there.`);
|
|
5761
5763
|
}
|
|
5764
|
+
}),
|
|
5765
|
+
T({
|
|
5766
|
+
version: "0.50.0",
|
|
5767
|
+
id: "0.50.0/01_transfer-runs-endpoint",
|
|
5768
|
+
title: "`GET /_voltro/admin/imports` → `GET /_voltro/admin/transfers`",
|
|
5769
|
+
kind: "manual",
|
|
5770
|
+
reach: "beyond-source",
|
|
5771
|
+
appliesTo: ({ text: e }) => e.find("/_voltro/admin/imports").length > 0,
|
|
5772
|
+
note: [
|
|
5773
|
+
"The read-only endpoint that reports data-transfer runs has moved:",
|
|
5774
|
+
"",
|
|
5775
|
+
" GET /_voltro/admin/imports → GET /_voltro/admin/transfers",
|
|
5776
|
+
"",
|
|
5777
|
+
"Same secret, same query parameters, same response shape. Only the path changed.",
|
|
5778
|
+
"",
|
|
5779
|
+
"It answers for four directions now — import, export, native backup, native",
|
|
5780
|
+
"restore — and it named one of them. The subcommand and the table were renamed",
|
|
5781
|
+
"for that reason in 0.49.0; this path was left behind, which is worse than not",
|
|
5782
|
+
"renaming any of them: a reader who followed the rename tries the matching path,",
|
|
5783
|
+
"gets a 404, and concludes the instance is too old.",
|
|
5784
|
+
"",
|
|
5785
|
+
"`voltro data transfers --target api` uses the new path for you. What needs your",
|
|
5786
|
+
"hand is anything that calls it directly — a monitoring check, an uptime probe,",
|
|
5787
|
+
"a curl in a runbook, a dashboard datasource."
|
|
5788
|
+
].join("\n")
|
|
5789
|
+
}),
|
|
5790
|
+
T({
|
|
5791
|
+
version: "0.50.0",
|
|
5792
|
+
id: "0.50.0/02_reissue-data-imports-rename",
|
|
5793
|
+
title: "RE-CHECK: `voltro data imports` → `voltro data transfers` (0.49.0's note could not see your scripts)",
|
|
5794
|
+
kind: "manual",
|
|
5795
|
+
reach: "beyond-source",
|
|
5796
|
+
note: [
|
|
5797
|
+
"This is a re-check of a rename that shipped in 0.49.0, not a new change.",
|
|
5798
|
+
"",
|
|
5799
|
+
" voltro data imports → voltro data transfers",
|
|
5800
|
+
"",
|
|
5801
|
+
"The 0.49.0 note that announced it only searched `.ts` / `.tsx` files — while",
|
|
5802
|
+
"the command it is about lives in shell scripts, CI job definitions, Makefiles",
|
|
5803
|
+
"and runbooks. So a project whose only occurrence was in, say, `.gitlab-ci.yml`",
|
|
5804
|
+
"was told there was nothing to apply.",
|
|
5805
|
+
"",
|
|
5806
|
+
"A codemod runs once, on the jump that crosses its version, so that note cannot",
|
|
5807
|
+
"be corrected for anyone already past 0.49.0. Hence this one. Search your",
|
|
5808
|
+
"pipelines, deploy jobs and runbooks for `voltro data imports` now:",
|
|
5809
|
+
"",
|
|
5810
|
+
" grep -rn \"voltro data imports\" .",
|
|
5811
|
+
"",
|
|
5812
|
+
"The search `voltro update` runs is wider from 0.50.0 on (every text file git",
|
|
5813
|
+
"tracks, not just source), but it still only reaches THIS repository — a step",
|
|
5814
|
+
"defined in a CI runner's own UI, a CronJob spec in another repo, or a wiki",
|
|
5815
|
+
"page is out of its reach by construction.",
|
|
5816
|
+
"",
|
|
5817
|
+
"Nothing else is required: the four renamed `@voltro/database` exports were",
|
|
5818
|
+
"rewritten for you in 0.49.0, and the table carries a rename marker, so",
|
|
5819
|
+
"`voltro db apply` moved `_voltro_data_imports` → `_voltro_data_transfers`",
|
|
5820
|
+
"with its rows."
|
|
5821
|
+
].join("\n")
|
|
5822
|
+
}),
|
|
5823
|
+
T({
|
|
5824
|
+
version: "0.50.0",
|
|
5825
|
+
id: "0.50.0/03_interrupted-replace-tables-nullable",
|
|
5826
|
+
title: "`InterruptedReplace.tables` is `number | null`",
|
|
5827
|
+
kind: "manual",
|
|
5828
|
+
appliesTo: ({ text: e }) => e.find("InterruptedReplace").length > 0,
|
|
5829
|
+
note: [
|
|
5830
|
+
"`InterruptedReplace.tables` is now `number | null`:",
|
|
5831
|
+
"",
|
|
5832
|
+
" readonly tables: number → readonly tables: number | null",
|
|
5833
|
+
"",
|
|
5834
|
+
"If you read it, `tsc` will point at the site. `null` means a native `voltro",
|
|
5835
|
+
"data restore` wrote the marker: it replaces the whole database from an",
|
|
5836
|
+
"artefact, so a table count is neither known up front nor meaningful after.",
|
|
5837
|
+
"Read `kind` (a new field, `'replace'` | `'restore'`) when you need to tell",
|
|
5838
|
+
"the two apart.",
|
|
5839
|
+
"",
|
|
5840
|
+
"The framework's own refusal text handles it by saying \"this database\" instead",
|
|
5841
|
+
"of a count. If you render your own, that is the shape to copy — the one thing",
|
|
5842
|
+
"not to do is coerce it, because `Number(null)` is `0` and the sentence then",
|
|
5843
|
+
"reports a measurement that was never taken."
|
|
5844
|
+
].join("\n")
|
|
5845
|
+
}),
|
|
5846
|
+
T({
|
|
5847
|
+
version: "0.51.0",
|
|
5848
|
+
id: "0.51.0/01_computed-source-keeps-its-names",
|
|
5849
|
+
title: "A COMPUTED `source:` can keep its typed names — do not widen the whole registry",
|
|
5850
|
+
kind: "manual",
|
|
5851
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5852
|
+
let t = e.getFullText();
|
|
5853
|
+
return /\bsource\s*:/.test(t) || t.includes("ReactivitySourceValue");
|
|
5854
|
+
}),
|
|
5855
|
+
note: [
|
|
5856
|
+
"A follow-up to the 0.45.0 note about `source:` being typed against your",
|
|
5857
|
+
"tables. Nothing changed in this release — this covers a case that note did",
|
|
5858
|
+
"not, and it can only be sent once, so it is sent to everyone.",
|
|
5859
|
+
"",
|
|
5860
|
+
"THE CASE",
|
|
5861
|
+
"",
|
|
5862
|
+
"A GENERIC reader — the table is chosen by the caller out of a registry, or",
|
|
5863
|
+
"arrives in `input` — is computed, and yet every name it can produce is known:",
|
|
5864
|
+
"",
|
|
5865
|
+
" source: Object.keys(JUNCTION_REGISTRY), // string[] — not assignable",
|
|
5866
|
+
"",
|
|
5867
|
+
"The 0.45.0 note offers two remedies and neither fits. There is no misspelled",
|
|
5868
|
+
"name to fix. And widening — `ReactivitySourceValue`, or `as TableName[]` —",
|
|
5869
|
+
"compiles at once while removing that entire registry from the check. It is",
|
|
5870
|
+
"the easiest thing to reach for and it opts out wholesale, silently.",
|
|
5871
|
+
"",
|
|
5872
|
+
"WHAT TO DO INSTEAD",
|
|
5873
|
+
"",
|
|
5874
|
+
"Keep the literal’s keys instead of recovering them afterwards:",
|
|
5875
|
+
"",
|
|
5876
|
+
" const REGISTRY = { … } as const satisfies Readonly<Record<string, JunctionMeta>>",
|
|
5877
|
+
"",
|
|
5878
|
+
" // consumers that index with a plain string still get the wide type",
|
|
5879
|
+
" export const JUNCTION_REGISTRY: Readonly<Record<string, JunctionMeta>> = REGISTRY",
|
|
5880
|
+
"",
|
|
5881
|
+
" // … and the source list keeps its literal names",
|
|
5882
|
+
" export const JUNCTION_TABLE_NAMES = Object.keys(REGISTRY) as ReadonlyArray<keyof typeof REGISTRY>",
|
|
5883
|
+
"",
|
|
5884
|
+
"THE PART THAT IS EASY TO GET WRONG",
|
|
5885
|
+
"",
|
|
5886
|
+
"An ANNOTATION widens the keys back, even when the literal carries",
|
|
5887
|
+
"`as const`. A registry written as",
|
|
5888
|
+
"",
|
|
5889
|
+
" export const R: Readonly<Record<string, Meta>> = { … } as const",
|
|
5890
|
+
"",
|
|
5891
|
+
"has `keyof typeof R === string`, and nothing about it looks wrong: the",
|
|
5892
|
+
"annotation is checked against the literal and then replaces its type.",
|
|
5893
|
+
"`satisfies` checks WITHOUT replacing. That is the whole difference, and it",
|
|
5894
|
+
"is why the two-line split above exists rather than one declaration.",
|
|
5895
|
+
"",
|
|
5896
|
+
"`ReactivitySourceValue` stays correct for a source that genuinely is not a",
|
|
5897
|
+
"literal — a name read from a config file, or one assembled at runtime. Use it",
|
|
5898
|
+
"when there is no key set to keep, not when recovering one takes two lines."
|
|
5899
|
+
].join("\n")
|
|
5762
5900
|
})
|
|
5763
|
-
],
|
|
5901
|
+
], Y = /* @__PURE__ */ new Set([
|
|
5764
5902
|
"node_modules",
|
|
5765
5903
|
".git",
|
|
5766
5904
|
"dist",
|
|
@@ -5772,73 +5910,137 @@ var x = (e, t, n) => {
|
|
|
5772
5910
|
".turbo",
|
|
5773
5911
|
".cache",
|
|
5774
5912
|
".output"
|
|
5775
|
-
]),
|
|
5913
|
+
]), xi = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, Si = /\.(?:tsx?|jsx?|mjs|cjs)$/, Ci = (e) => g(e).replace(Si, ""), wi = (e) => {
|
|
5776
5914
|
let t = e.split("/").filter((e) => e.length > 0).pop();
|
|
5777
5915
|
if (t === void 0 || t === "." || t === "..") return;
|
|
5778
|
-
let n = t.replace(
|
|
5916
|
+
let n = t.replace(Si, "");
|
|
5779
5917
|
return n.length > 0 ? n : void 0;
|
|
5780
|
-
},
|
|
5918
|
+
}, Ti = (e) => {
|
|
5781
5919
|
let t = /* @__PURE__ */ new Map();
|
|
5782
5920
|
for (let n of e) {
|
|
5783
5921
|
let e;
|
|
5784
5922
|
try {
|
|
5785
|
-
e =
|
|
5923
|
+
e = b(n, "utf8");
|
|
5786
5924
|
} catch {
|
|
5787
5925
|
continue;
|
|
5788
5926
|
}
|
|
5789
|
-
|
|
5790
|
-
for (let r =
|
|
5791
|
-
let e =
|
|
5927
|
+
X.lastIndex = 0;
|
|
5928
|
+
for (let r = X.exec(e); r !== null; r = X.exec(e)) {
|
|
5929
|
+
let e = wi(r[1] ?? r[2] ?? "");
|
|
5792
5930
|
if (e === void 0) continue;
|
|
5793
5931
|
let i = t.get(e);
|
|
5794
5932
|
i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
|
|
5795
5933
|
}
|
|
5796
5934
|
}
|
|
5797
5935
|
return t;
|
|
5798
|
-
},
|
|
5936
|
+
}, Ei = (e, t) => {
|
|
5799
5937
|
let n = new Set(e);
|
|
5800
|
-
for (let r of e) for (let e of [
|
|
5938
|
+
for (let r of e) for (let e of [Ci(r), g(_(r))]) {
|
|
5801
5939
|
let r = t.get(e);
|
|
5802
5940
|
if (r !== void 0) for (let e of r) n.add(e);
|
|
5803
5941
|
}
|
|
5804
5942
|
return n;
|
|
5805
|
-
},
|
|
5943
|
+
}, Di = /* @__PURE__ */ ".ts,.tsx,.mts,.cts,.js,.jsx,.mjs,.cjs,.yml,.yaml,.toml,.json,.jsonc,.ini,.cfg,.conf,.sh,.bash,.zsh,.fish,.ps1,.bat,.cmd,.md,.mdx,.rst,.txt,.adoc,Makefile,makefile,GNUmakefile,Justfile,justfile,Dockerfile,Containerfile,Procfile,Jenkinsfile,Rakefile,Taskfile".split(","), Oi = 2097152, ki = 4e4, Ai = (e) => {
|
|
5944
|
+
let t = e.slice(e.lastIndexOf("/") + 1);
|
|
5945
|
+
return Di.some((e) => e.startsWith(".") ? t.endsWith(e) : t === e);
|
|
5946
|
+
}, ji = (e) => {
|
|
5947
|
+
try {
|
|
5948
|
+
let t = ne("git", [
|
|
5949
|
+
"ls-files",
|
|
5950
|
+
"--cached",
|
|
5951
|
+
"--others",
|
|
5952
|
+
"--exclude-standard"
|
|
5953
|
+
], {
|
|
5954
|
+
cwd: e,
|
|
5955
|
+
encoding: "utf8",
|
|
5956
|
+
maxBuffer: 268435456,
|
|
5957
|
+
stdio: [
|
|
5958
|
+
"ignore",
|
|
5959
|
+
"pipe",
|
|
5960
|
+
"ignore"
|
|
5961
|
+
]
|
|
5962
|
+
}).split("\n").filter((e) => e.length > 0);
|
|
5963
|
+
return t.length > 0 ? t : void 0;
|
|
5964
|
+
} catch {
|
|
5965
|
+
return;
|
|
5966
|
+
}
|
|
5967
|
+
}, Z = (e, t = ji) => {
|
|
5968
|
+
let n = t(e);
|
|
5969
|
+
if (n === void 0) return Mi;
|
|
5970
|
+
let r = /* @__PURE__ */ new Map(), i = [];
|
|
5971
|
+
for (let t of n) {
|
|
5972
|
+
if (t.split("/").some((e) => Y.has(e)) || !Ai(t)) continue;
|
|
5973
|
+
if (r.size >= ki) {
|
|
5974
|
+
i.push(t);
|
|
5975
|
+
continue;
|
|
5976
|
+
}
|
|
5977
|
+
let n = v(e, t);
|
|
5978
|
+
try {
|
|
5979
|
+
if (te(n).size > Oi) {
|
|
5980
|
+
i.push(t);
|
|
5981
|
+
continue;
|
|
5982
|
+
}
|
|
5983
|
+
let e = b(n, "utf8");
|
|
5984
|
+
if (e.includes("\0")) {
|
|
5985
|
+
i.push(t);
|
|
5986
|
+
continue;
|
|
5987
|
+
}
|
|
5988
|
+
r.set(t, e);
|
|
5989
|
+
} catch {
|
|
5990
|
+
i.push(t);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
return {
|
|
5994
|
+
find: (e) => {
|
|
5995
|
+
if (e === "") return [];
|
|
5996
|
+
let t = [];
|
|
5997
|
+
for (let [n, i] of r) i.includes(e) && t.push(n);
|
|
5998
|
+
return t;
|
|
5999
|
+
},
|
|
6000
|
+
scanned: r.size,
|
|
6001
|
+
skipped: i
|
|
6002
|
+
};
|
|
6003
|
+
}, Mi = {
|
|
6004
|
+
find: () => [],
|
|
6005
|
+
scanned: 0,
|
|
6006
|
+
skipped: []
|
|
6007
|
+
}, Ni = [
|
|
5806
6008
|
"**/*.ts",
|
|
5807
6009
|
"**/*.tsx",
|
|
5808
6010
|
"!**/node_modules/**",
|
|
5809
6011
|
"!**/dist/**",
|
|
5810
6012
|
"!**/.framework/**",
|
|
5811
6013
|
"!**/*.d.ts"
|
|
5812
|
-
], Q = (e, t, n =
|
|
6014
|
+
], Q = (e, t, n = Ii) => {
|
|
5813
6015
|
let r = /* @__PURE__ */ new Set();
|
|
5814
|
-
for (let e of t) for (let t of e.scope ??
|
|
6016
|
+
for (let e of t) for (let t of e.scope ?? Ni) r.add(t);
|
|
5815
6017
|
let i = n(e);
|
|
5816
6018
|
if (i !== void 0) {
|
|
5817
|
-
let t = [...r].filter((e) => !e.startsWith("!")).map((e) =>
|
|
6019
|
+
let t = [...r].filter((e) => !e.startsWith("!")).map((e) => Fi(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => Fi(e.slice(1))), a = [...Y], o = [];
|
|
5818
6020
|
for (let r of i) {
|
|
5819
6021
|
if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
|
|
5820
|
-
let i =
|
|
5821
|
-
|
|
6022
|
+
let i = v(e, r);
|
|
6023
|
+
y(i) && o.push(i);
|
|
5822
6024
|
}
|
|
5823
|
-
return
|
|
6025
|
+
return Pi(o.length, e), o;
|
|
5824
6026
|
}
|
|
5825
|
-
let a =
|
|
6027
|
+
let a = ae([...r], {
|
|
5826
6028
|
cwd: e,
|
|
5827
6029
|
absolute: !0,
|
|
5828
6030
|
expandDirectories: !1,
|
|
5829
6031
|
followSymbolicLinks: !1,
|
|
5830
|
-
ignore: [...
|
|
6032
|
+
ignore: [...xi()]
|
|
5831
6033
|
});
|
|
5832
|
-
return
|
|
5833
|
-
},
|
|
5834
|
-
let n =
|
|
6034
|
+
return Pi(a.length, e), a;
|
|
6035
|
+
}, Pi = (e, t) => {
|
|
6036
|
+
let n = Li();
|
|
5835
6037
|
if (!(e <= n)) throw Error(`voltro update: the codemod scan matched ${e} files under ${t}, past the ${n} ceiling.\n Every matched file is parsed into one ts-morph project, so the heap needed grows with\n this number — past the ceiling it runs out of memory, and V8 aborts without naming a\n limit, which is why this stops here instead. Three ways out, in order:\n • put non-source directories in .gitignore (inside a git repo the scan then never\n sees them at all — a build cache, a data dump, a virtualenv),\n • run \`voltro update\` from the app directory rather than the workspace root, or\n • take one codemod at a time: \`voltro update --dry-run\` lists the ids, then\n \`voltro update --codemods-only --only <id>\` runs a single one.\n If every one of those files really is source this pass must rewrite, raise the ceiling\n with VOLTRO_CODEMOD_MAX_FILES and give node room: NODE_OPTIONS=--max-old-space-size=8192.`);
|
|
5836
|
-
},
|
|
6038
|
+
}, Fi = (e) => {
|
|
5837
6039
|
let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
|
|
5838
6040
|
return (e) => RegExp(`^${t}$`).test(e);
|
|
5839
|
-
},
|
|
6041
|
+
}, Ii = (e) => {
|
|
5840
6042
|
try {
|
|
5841
|
-
let t =
|
|
6043
|
+
let t = ne("git", [
|
|
5842
6044
|
"ls-files",
|
|
5843
6045
|
"--cached",
|
|
5844
6046
|
"--others",
|
|
@@ -5857,7 +6059,7 @@ var x = (e, t, n) => {
|
|
|
5857
6059
|
} catch {
|
|
5858
6060
|
return;
|
|
5859
6061
|
}
|
|
5860
|
-
},
|
|
6062
|
+
}, Li = () => {
|
|
5861
6063
|
let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
|
|
5862
6064
|
return Number.isInteger(e) && e > 0 ? e : 2e4;
|
|
5863
6065
|
}, $ = (e, t) => {
|
|
@@ -5867,54 +6069,59 @@ var x = (e, t, n) => {
|
|
|
5867
6069
|
if (t !== 0) return t;
|
|
5868
6070
|
}
|
|
5869
6071
|
return 0;
|
|
5870
|
-
},
|
|
5871
|
-
let
|
|
5872
|
-
for (let
|
|
5873
|
-
|
|
5874
|
-
|
|
6072
|
+
}, Ri = (e, t, n = bi) => n.filter((n) => $(e, n.version) < 0 && $(n.version, t) <= 0).slice().sort(me), zi = (e, t, n = () => {}, r = Mi) => {
|
|
6073
|
+
let i = [], a = [], o = [];
|
|
6074
|
+
for (let s of t) {
|
|
6075
|
+
let t = !s.appliesTo || s.appliesTo({
|
|
6076
|
+
project: e,
|
|
6077
|
+
text: r
|
|
6078
|
+
}), c = s.kind === "manual" && s.reach === "beyond-source";
|
|
6079
|
+
if (!t && !c) {
|
|
6080
|
+
o.push(s.id);
|
|
5875
6081
|
continue;
|
|
5876
6082
|
}
|
|
5877
|
-
if (
|
|
5878
|
-
|
|
5879
|
-
id:
|
|
5880
|
-
title:
|
|
5881
|
-
note:
|
|
6083
|
+
if (s.kind === "manual") {
|
|
6084
|
+
a.push({
|
|
6085
|
+
id: s.id,
|
|
6086
|
+
title: s.title,
|
|
6087
|
+
note: s.note ?? "",
|
|
6088
|
+
certain: t
|
|
5882
6089
|
});
|
|
5883
6090
|
continue;
|
|
5884
6091
|
}
|
|
5885
|
-
let
|
|
5886
|
-
|
|
6092
|
+
let l = new Map(e.getSourceFiles().map((e) => [e.getFilePath(), e.getFullText()]));
|
|
6093
|
+
s.transform?.({
|
|
5887
6094
|
project: e,
|
|
5888
|
-
helpers:
|
|
6095
|
+
helpers: fe,
|
|
5889
6096
|
log: n
|
|
5890
6097
|
});
|
|
5891
|
-
let
|
|
5892
|
-
for (let [e,
|
|
5893
|
-
let
|
|
5894
|
-
for (let [e,
|
|
5895
|
-
if (
|
|
5896
|
-
let
|
|
5897
|
-
|
|
6098
|
+
let u = new Map(e.getSourceFiles().map((e) => [e.getFilePath(), e.getFullText()])), d = /* @__PURE__ */ new Map();
|
|
6099
|
+
for (let [e, t] of l) u.has(e) || d.set(t, e);
|
|
6100
|
+
let f = [];
|
|
6101
|
+
for (let [e, t] of u) {
|
|
6102
|
+
if (l.has(e)) continue;
|
|
6103
|
+
let n = d.get(t);
|
|
6104
|
+
f.push(n === void 0 ? e : `${n} → ${e}`);
|
|
5898
6105
|
}
|
|
5899
|
-
let
|
|
5900
|
-
for (let
|
|
5901
|
-
let e =
|
|
5902
|
-
e !== void 0 && e !==
|
|
6106
|
+
let p = [];
|
|
6107
|
+
for (let t of e.getSourceFiles()) {
|
|
6108
|
+
let e = l.get(t.getFilePath());
|
|
6109
|
+
e !== void 0 && e !== t.getFullText() && p.push(t.getFilePath());
|
|
5903
6110
|
}
|
|
5904
|
-
|
|
5905
|
-
id:
|
|
5906
|
-
title:
|
|
5907
|
-
changedFiles:
|
|
5908
|
-
renamedFiles:
|
|
6111
|
+
i.push({
|
|
6112
|
+
id: s.id,
|
|
6113
|
+
title: s.title,
|
|
6114
|
+
changedFiles: p,
|
|
6115
|
+
renamedFiles: f
|
|
5909
6116
|
});
|
|
5910
6117
|
}
|
|
5911
6118
|
return {
|
|
5912
|
-
applied:
|
|
5913
|
-
manual:
|
|
5914
|
-
skipped:
|
|
6119
|
+
applied: i,
|
|
6120
|
+
manual: a,
|
|
6121
|
+
skipped: o
|
|
5915
6122
|
};
|
|
5916
|
-
},
|
|
5917
|
-
let t = e.log ?? (() => {}), n =
|
|
6123
|
+
}, Bi = async (e) => {
|
|
6124
|
+
let t = e.log ?? (() => {}), n = Ri(e.from, e.to, e.codemods ?? bi), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
|
|
5918
6125
|
let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
|
|
5919
6126
|
if (r.length > 0) throw Error(`voltro update --only: no codemod in ${e.from} → ${e.to} has id ${r.map((e) => `"${e}"`).join(", ")}.\n Available for this jump:\n${n.map((e) => ` ${e.id}`).join("\n")}`);
|
|
5920
6127
|
return t;
|
|
@@ -5926,31 +6133,39 @@ var x = (e, t, n) => {
|
|
|
5926
6133
|
};
|
|
5927
6134
|
let i = f(e.root);
|
|
5928
6135
|
i !== void 0 && t(`path aliases: ${Object.keys(i.paths).join(", ")}`);
|
|
5929
|
-
let a = () => new
|
|
6136
|
+
let a = () => new re({
|
|
5930
6137
|
skipAddingFilesFromTsConfig: !0,
|
|
5931
|
-
manipulationSettings: { quoteKind:
|
|
6138
|
+
manipulationSettings: { quoteKind: ie.Single },
|
|
5932
6139
|
...i === void 0 ? {} : { compilerOptions: {
|
|
5933
6140
|
baseUrl: i.baseUrl,
|
|
5934
6141
|
paths: i.paths
|
|
5935
6142
|
} }
|
|
5936
|
-
}), o, s = Q(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p =
|
|
6143
|
+
}), o, s = Q(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p, m = {
|
|
6144
|
+
find: (t) => (p ??= Z(e.root)).find(t),
|
|
6145
|
+
get scanned() {
|
|
6146
|
+
return (p ??= Z(e.root)).scanned;
|
|
6147
|
+
},
|
|
6148
|
+
get skipped() {
|
|
6149
|
+
return (p ??= Z(e.root)).skipped;
|
|
6150
|
+
}
|
|
6151
|
+
}, h = c.length > 0 ? Ti(Q(e.root, [{
|
|
5937
6152
|
...c[0],
|
|
5938
|
-
scope:
|
|
6153
|
+
scope: Ni
|
|
5939
6154
|
}])) : void 0;
|
|
5940
6155
|
for (let n of r) {
|
|
5941
6156
|
if (c.includes(n)) {
|
|
5942
6157
|
let r = a(), i = Q(e.root, [n]);
|
|
5943
|
-
for (let e of
|
|
5944
|
-
let s =
|
|
6158
|
+
for (let e of Ei(i, h)) y(e) && r.addSourceFileAtPath(e);
|
|
6159
|
+
let s = zi(r, [n], t, m);
|
|
5945
6160
|
e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
|
|
5946
6161
|
continue;
|
|
5947
6162
|
}
|
|
5948
6163
|
o ??= (() => {
|
|
5949
6164
|
let e = a();
|
|
5950
|
-
for (let t of s)
|
|
6165
|
+
for (let t of s) y(t) && e.addSourceFileAtPath(t);
|
|
5951
6166
|
return e;
|
|
5952
6167
|
})();
|
|
5953
|
-
let r =
|
|
6168
|
+
let r = zi(o, [n], t, m);
|
|
5954
6169
|
e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
|
|
5955
6170
|
}
|
|
5956
6171
|
return {
|
|
@@ -5960,4 +6175,4 @@ var x = (e, t, n) => {
|
|
|
5960
6175
|
};
|
|
5961
6176
|
};
|
|
5962
6177
|
//#endregion
|
|
5963
|
-
export {
|
|
6178
|
+
export { T as i, Bi as n, Y as r, $ as t };
|