@voltro/cli 0.37.0 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/dist/{apiBuild-DGsShRuF.js → apiBuild-CvacV4zA.js} +2 -2
- package/dist/apiBuild-ttSDDOGp.js +2 -0
- package/dist/bin.js +1 -1
- package/dist/bootRefusal-pgvDxlrU.js +7 -0
- package/dist/{build-CiFA-E19.js → build-_LoTC1s0.js} +30 -30
- package/dist/{checkCommand-CLPTzYqM.js → checkCommand-Pjk5sBl2.js} +2 -2
- package/dist/{checkCommand-CIPYyNyc.js → checkCommand-wibtQx-N.js} +1 -1
- package/dist/{codegenCommand-BxS3sCeJ.js → codegenCommand-C60m_LCp.js} +1 -1
- package/dist/{codemodRunner-cyqu5oxr.js → codemodRunner-CXgQ-ecJ.js} +314 -201
- package/dist/{commands-BwG9A9Vv.js → commands-DaZVi9wi.js} +20 -20
- package/dist/{dashboardCommand-C7U146xQ.js → dashboardCommand-Dxy_kmoW.js} +3 -3
- package/dist/{dataCommand-BOwd8_aJ.js → dataCommand-D6-Ha2lf.js} +1 -1
- package/dist/{dev-BV6XNYKf.js → dev-CAOoXOkc.js} +1 -1
- package/dist/{dev-bwDgSObH.js → dev-E7Eo5GHc.js} +261 -260
- package/dist/doctorCommand-Cjl8Yii8.js +2 -0
- package/dist/{doctorCommand-BXyrtN2l.js → doctorCommand-gmDyALFL.js} +10 -10
- package/dist/{dormancyCommand-BDdRRLsa.js → dormancyCommand-CE6wG8lj.js} +1 -1
- package/dist/{embeddingsCommand-CjWGcAE6.js → embeddingsCommand-C3uAuf6N.js} +1 -1
- package/dist/{envCommand-B_TeG297.js → envCommand-DzuWh8ya.js} +19 -19
- package/dist/{evolveCommand-D6N-WfA4.js → evolveCommand-DPLqcYb2.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{infoCommand-BHKTprrx.js → infoCommand-CmczGj_l.js} +1 -1
- package/dist/{runtimeTrace-WYV7qYvz.js → runtimeTrace-6jfznSzr.js} +1 -1
- package/dist/{sdkgen-TM5sSdf7.js → sdkgen-CEJZZQUA.js} +1 -1
- package/dist/serveCommand-DNRF2nDI.js +2 -0
- package/dist/{serveCommand-CyZsXL1l.js → serveCommand-DnY4VS3D.js} +541 -540
- package/dist/serveEntry.js +1 -1
- package/dist/{start-BgN62boB.js → start-CidwCcjl.js} +1 -1
- package/dist/{start-T4VesWiM.js → start-RCh6qKFe.js} +24 -24
- package/dist/startEntry.js +1 -1
- package/dist/startup.d.ts +40 -1
- package/dist/startup.js +2 -2
- package/dist/startupRunner-CEqQq7ax.js +108 -0
- package/dist/updateCommand-B0J_T9Vs.js +2 -0
- package/dist/{updateCommand-9GHHsmDf.js → updateCommand-CmksX7m_.js} +1 -1
- package/dist/{webDev-Dybxew86.js → webDev-BKdD7c_a.js} +689 -691
- package/dist/webDev-CcJfdSjl2.js +2 -0
- package/dist/{webhooksCommand-CHWSPk5z.js → webhooksCommand-BHeaF1ZJ.js} +1 -1
- package/package.json +29 -17
- package/templates/AGENTS.md +1 -1
- package/templates/agent-docs/_index.md +1 -1
- package/templates/agent-docs/plugins/audit.md +4 -0
- package/templates/agent-docs/plugins/auth.md +4 -6
- package/templates/agent-docs/reference.md +3 -2
- package/templates/agent-docs/whats-new.md +49 -47
- 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-cms/tests/content.write.test.ts +16 -2
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +11 -5
- package/templates/apps/api-collab/package.json +8 -8
- package/templates/apps/api-collab/tests/documents.setBody.test.ts +8 -2
- 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/mutations/documents.update.mutation.server.ts +7 -5
- 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 +3 -3
- package/templates/patches/@effect__cluster@0.60.0.patch +51 -3
- package/dist/apiBuild-D0f6hYbH.js +0 -2
- package/dist/doctorCommand-C4L61G_L.js +0 -2
- package/dist/serveCommand-qumm7jdF.js +0 -2
- package/dist/startupRunner-DPGFchOa.js +0 -71
- package/dist/updateCommand-BN0tkl9-.js +0 -2
- package/dist/webDev-BcykISYQ2.js +0 -2
|
@@ -2764,11 +2764,11 @@ var x = (e, t, n) => {
|
|
|
2764
2764
|
"that legitimately resolves to `undefined` does not throw — only a missing",
|
|
2765
2765
|
"`loader` does."
|
|
2766
2766
|
].join("\n")
|
|
2767
|
-
}), U = "@voltro/ui-shadcn", W = "voltro:lang", hn = "voltro:locale",
|
|
2767
|
+
}), U = "@voltro/ui-shadcn", W = "voltro:lang", hn = "voltro:locale", gn = "parsePreferenceCookies", _n = (e, t) => {
|
|
2768
2768
|
let n = [];
|
|
2769
2769
|
for (let r of e.getImportDeclarations()) if (r.getModuleSpecifierValue() === U) for (let e of r.getNamedImports()) e.getName() === t && n.push(e.getAliasNode()?.getText() ?? e.getName());
|
|
2770
2770
|
return n;
|
|
2771
|
-
},
|
|
2771
|
+
}, vn = (e) => {
|
|
2772
2772
|
let t = 0;
|
|
2773
2773
|
for (let n of e.getSourceFiles()) n.getFullText().includes(W) && n.forEachDescendant((e) => {
|
|
2774
2774
|
if (y.isStringLiteral(e) || y.isNoSubstitutionTemplateLiteral(e) || y.isTemplateHead(e) || y.isTemplateMiddle(e) || y.isTemplateTail(e) || y.isRegularExpressionLiteral(e)) {
|
|
@@ -2778,10 +2778,10 @@ var x = (e, t, n) => {
|
|
|
2778
2778
|
}
|
|
2779
2779
|
});
|
|
2780
2780
|
return t;
|
|
2781
|
-
},
|
|
2781
|
+
}, yn = (e) => {
|
|
2782
2782
|
let t = 0, n = 0;
|
|
2783
2783
|
for (let r of e.getSourceFiles()) {
|
|
2784
|
-
let e =
|
|
2784
|
+
let e = _n(r, gn);
|
|
2785
2785
|
e.length !== 0 && r.forEachDescendant((r) => {
|
|
2786
2786
|
if (!y.isCallExpression(r) || !e.includes(r.getExpression().getText())) return;
|
|
2787
2787
|
let i = r.getParent();
|
|
@@ -2804,29 +2804,29 @@ var x = (e, t, n) => {
|
|
|
2804
2804
|
access: t,
|
|
2805
2805
|
destructured: n
|
|
2806
2806
|
};
|
|
2807
|
-
},
|
|
2807
|
+
}, bn = C({
|
|
2808
2808
|
version: "0.30.0",
|
|
2809
2809
|
id: "0.30.0/01_locale-cookie-rename",
|
|
2810
2810
|
title: "the language-preference cookie is `voltro:locale` (was `voltro:lang`)",
|
|
2811
2811
|
kind: "transform",
|
|
2812
2812
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2813
2813
|
let t = e.getFullText();
|
|
2814
|
-
return t.includes(W) || t.includes(U) && (t.includes("LANG_COOKIE") || t.includes(
|
|
2814
|
+
return t.includes(W) || t.includes(U) && (t.includes("LANG_COOKIE") || t.includes(gn));
|
|
2815
2815
|
}),
|
|
2816
2816
|
transform: ({ project: e, helpers: t, log: n }) => {
|
|
2817
2817
|
let r = t.renameImport(e, {
|
|
2818
2818
|
module: U,
|
|
2819
2819
|
from: "LANG_COOKIE",
|
|
2820
2820
|
to: "LOCALE_COOKIE"
|
|
2821
|
-
}), i =
|
|
2821
|
+
}), i = vn(e), { access: a, destructured: o } = yn(e), s = [];
|
|
2822
2822
|
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
2823
|
}
|
|
2824
|
-
}),
|
|
2824
|
+
}), xn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, Sn = C({
|
|
2825
2825
|
version: "0.30.0",
|
|
2826
2826
|
id: "0.30.0/02_audit-purge-query-recheck",
|
|
2827
2827
|
title: "re-run the credential-purge query — the 0.28.0 spelling under-reported on MySQL/MariaDB",
|
|
2828
2828
|
kind: "manual",
|
|
2829
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
2829
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => xn.test(e.getFullText())),
|
|
2830
2830
|
note: [
|
|
2831
2831
|
"IF YOU ALREADY RAN THE 0.28.0 PURGE CHECK ON MySQL OR MariaDB, RUN IT AGAIN.",
|
|
2832
2832
|
"The query that note shipped under-reported, and it under-reported by",
|
|
@@ -2873,14 +2873,14 @@ var x = (e, t, n) => {
|
|
|
2873
2873
|
" `connectionCredentials(...)` keeps it in the framework vault",
|
|
2874
2874
|
" (see `0.28.0/03_atlassian-credentials-context`)."
|
|
2875
2875
|
].join("\n")
|
|
2876
|
-
}),
|
|
2876
|
+
}), Cn = /\.webhooks\s*\.\s*emit\s*\(|useWebhooks\s*\(/, wn = C({
|
|
2877
2877
|
version: "0.30.0",
|
|
2878
2878
|
id: "0.30.0/03_webhook-emit-durable-deferral",
|
|
2879
2879
|
title: "a deferred `webhooks.emit` rides the transactional outbox; `{ immediate: true }` opts out",
|
|
2880
2880
|
kind: "manual",
|
|
2881
2881
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2882
2882
|
let t = e.getFullText();
|
|
2883
|
-
return
|
|
2883
|
+
return Cn.test(t) || w("emit").test(t);
|
|
2884
2884
|
}),
|
|
2885
2885
|
note: [
|
|
2886
2886
|
"A `ctx.webhooks.emit(...)` inside a MUTATION now enqueues a transactional",
|
|
@@ -2930,14 +2930,14 @@ var x = (e, t, n) => {
|
|
|
2930
2930
|
"created by the declarative differ on the next `voltro dev` boot or",
|
|
2931
2931
|
"`voltro db apply` — there is nothing to run by hand."
|
|
2932
2932
|
].join("\n")
|
|
2933
|
-
}),
|
|
2933
|
+
}), Tn = /\.executionId\b|workflows\s*\.\s*start\s*\(|workflows\s*\.\s*child\s*\(/, En = C({
|
|
2934
2934
|
version: "0.30.0",
|
|
2935
2935
|
id: "0.30.0/04_workflow-run-handle-nullable-execution",
|
|
2936
2936
|
title: "`WorkflowRunHandle.executionId` is `string | null`; `status` gains queued/dropped/skipped",
|
|
2937
2937
|
kind: "manual",
|
|
2938
2938
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
2939
2939
|
let t = e.getFullText();
|
|
2940
|
-
return
|
|
2940
|
+
return Tn.test(t) || w("start").test(t);
|
|
2941
2941
|
}),
|
|
2942
2942
|
note: [
|
|
2943
2943
|
"A workflow start no longer always becomes a run. With declarative flow",
|
|
@@ -3002,12 +3002,12 @@ var x = (e, t, n) => {
|
|
|
3002
3002
|
" and `singleton: { mode: 'skip' }` throws `WorkflowSingletonHeldError`",
|
|
3003
3003
|
" carrying the incumbent's execution id, so you can wait on THAT run."
|
|
3004
3004
|
].join("\n")
|
|
3005
|
-
}),
|
|
3005
|
+
}), Dn = /auditPlugin\s*\(|versioningPlugin\s*\(|atlassianPlugin\s*\(/, On = C({
|
|
3006
3006
|
version: "0.31.0",
|
|
3007
3007
|
id: "0.31.0/01_rerun-credential-scan",
|
|
3008
3008
|
title: "re-run `voltro db scan-credentials` — on 0.30.0–0.30.2 it skipped the column credentials land in",
|
|
3009
3009
|
kind: "manual",
|
|
3010
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3010
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Dn.test(e.getFullText())),
|
|
3011
3011
|
note: [
|
|
3012
3012
|
"IF YOU RAN `voltro db scan-credentials` ON 0.30.0, 0.30.1 OR 0.30.2, RUN IT",
|
|
3013
3013
|
"AGAIN. Its answer did not cover the column a leaked credential is most",
|
|
@@ -3041,7 +3041,7 @@ var x = (e, t, n) => {
|
|
|
3041
3041
|
"credential on the Subject — `connectionCredentials(...)` keeps it in the",
|
|
3042
3042
|
"framework vault."
|
|
3043
3043
|
].join("\n")
|
|
3044
|
-
}),
|
|
3044
|
+
}), kn = C({
|
|
3045
3045
|
version: "0.32.0",
|
|
3046
3046
|
id: "0.32.0/01_audit-redacts-outcome",
|
|
3047
3047
|
title: "`auditPlugin` now redacts `outcome` by default — opt out if your trail reads it",
|
|
@@ -3082,7 +3082,7 @@ var x = (e, t, n) => {
|
|
|
3082
3082
|
"purge the rows AND rotate the credentials — assume anything written to a",
|
|
3083
3083
|
"log table has been read."
|
|
3084
3084
|
].join("\n")
|
|
3085
|
-
}),
|
|
3085
|
+
}), An = C({
|
|
3086
3086
|
version: "0.33.0",
|
|
3087
3087
|
id: "0.33.0/01_opentelemetry-optional-peer",
|
|
3088
3088
|
title: "`@effect/opentelemetry` is now an optional peer — install it if you use tracing",
|
|
@@ -3114,7 +3114,7 @@ var x = (e, t, n) => {
|
|
|
3114
3114
|
"never wrote down. It is a startup failure, not a silent loss of traces —",
|
|
3115
3115
|
"losing telemetry quietly would be the worse outcome."
|
|
3116
3116
|
].join("\n")
|
|
3117
|
-
}),
|
|
3117
|
+
}), jn = C({
|
|
3118
3118
|
version: "0.33.0",
|
|
3119
3119
|
id: "0.33.0/02_coordinated-handle-gained-wake",
|
|
3120
3120
|
title: "`CoordinatedScheduleHandle` gained `wake()`, `currentIntervalMs()` and `isArmed()` — update hand-written test doubles",
|
|
@@ -3164,7 +3164,7 @@ var x = (e, t, n) => {
|
|
|
3164
3164
|
"Reporting nothing is the conservative default on purpose — a task that",
|
|
3165
3165
|
"cannot tell whether it was idle must not be slowed down on a guess."
|
|
3166
3166
|
].join("\n")
|
|
3167
|
-
}),
|
|
3167
|
+
}), Mn = C({
|
|
3168
3168
|
version: "0.34.0",
|
|
3169
3169
|
id: "0.34.0/01_serve-does-mount-inspect",
|
|
3170
3170
|
title: "Correction: `voltro serve` DOES mount an inspect surface (the 0.12.0 note said it mounts none)",
|
|
@@ -3205,12 +3205,12 @@ var x = (e, t, n) => {
|
|
|
3205
3205
|
"place, everyone already past 0.12.0 — which is everyone the wrong sentence",
|
|
3206
3206
|
"could have misled — would never have seen it."
|
|
3207
3207
|
].join("\n")
|
|
3208
|
-
}),
|
|
3208
|
+
}), Nn = (e) => /\.with\(\s*[\w.]*tenant\(/.test(e) || /\.serverOnly\(\s*\)/.test(e), Pn = C({
|
|
3209
3209
|
version: "0.34.0",
|
|
3210
3210
|
id: "0.34.0/02_keyed-writes-are-tenant-scoped",
|
|
3211
3211
|
title: "Keyed-by-pk writes are tenant-enforced, and `crud` refuses `.serverOnly()` input",
|
|
3212
3212
|
kind: "manual",
|
|
3213
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3213
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Nn(e.getFullText())),
|
|
3214
3214
|
note: [
|
|
3215
3215
|
"TWO WRITE-SIDE REFUSALS ARE NEW. Neither changes a type, so `tsc` will not",
|
|
3216
3216
|
"point at the call sites — these are runtime behaviour changes.",
|
|
@@ -3274,12 +3274,12 @@ var x = (e, t, n) => {
|
|
|
3274
3274
|
" an attack look like a no-op and leaves an honest caller debugging a",
|
|
3275
3275
|
" value that quietly did not land."
|
|
3276
3276
|
].join("\n")
|
|
3277
|
-
}),
|
|
3277
|
+
}), Fn = /* @__PURE__ */ new Set([
|
|
3278
3278
|
"defineQuery",
|
|
3279
3279
|
"defineMutation",
|
|
3280
3280
|
"defineAction",
|
|
3281
3281
|
"defineStream"
|
|
3282
|
-
]),
|
|
3282
|
+
]), In = (e) => {
|
|
3283
3283
|
let t = e.getArguments()[0];
|
|
3284
3284
|
if (t === void 0 || !t.isKind(b.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
|
|
3285
3285
|
let n = /* @__PURE__ */ new Set();
|
|
@@ -3288,16 +3288,16 @@ var x = (e, t, n) => {
|
|
|
3288
3288
|
t !== void 0 && n.add(t);
|
|
3289
3289
|
}
|
|
3290
3290
|
return n;
|
|
3291
|
-
},
|
|
3292
|
-
if (!
|
|
3293
|
-
let t =
|
|
3291
|
+
}, Ln = (e, t) => /define(Query|Mutation|Action|Stream)\s*\(/.test(e) ? t.some((e) => {
|
|
3292
|
+
if (!Fn.has(e.getExpression().getText())) return !1;
|
|
3293
|
+
let t = In(e);
|
|
3294
3294
|
return !t.has("internal") && !t.has("guards") && !t.has("openAccess");
|
|
3295
|
-
}) : !1,
|
|
3295
|
+
}) : !1, Rn = C({
|
|
3296
3296
|
version: "0.34.0",
|
|
3297
3297
|
id: "0.34.0/03_procedure-access-decision",
|
|
3298
3298
|
title: "Every wire-exposed procedure declares an access decision (`guards:` or `openAccess:`)",
|
|
3299
3299
|
kind: "manual",
|
|
3300
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3300
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ln(e.getFullText(), e.getDescendantsOfKind(b.CallExpression))),
|
|
3301
3301
|
note: [
|
|
3302
3302
|
"YOUR APP WILL NOT BOOT UNTIL EVERY WIRE-EXPOSED PROCEDURE DECIDES WHO MAY",
|
|
3303
3303
|
"CALL IT. This is the single largest behaviour change in this release, and it",
|
|
@@ -3342,18 +3342,18 @@ var x = (e, t, n) => {
|
|
|
3342
3342
|
"procedures your PLUGINS declare — the gate reads your app's own discovered",
|
|
3343
3343
|
"files only, so adopting this does not turn into a plugin bug report."
|
|
3344
3344
|
].join("\n")
|
|
3345
|
-
}),
|
|
3345
|
+
}), zn = [
|
|
3346
3346
|
"workosAuthorizationUrl",
|
|
3347
3347
|
"workosAuthenticateWithCode",
|
|
3348
3348
|
"workosBeginLogin"
|
|
3349
|
-
],
|
|
3349
|
+
], Bn = C({
|
|
3350
3350
|
version: "0.34.0",
|
|
3351
3351
|
id: "0.34.0/04_workos-oauth-state-pkce",
|
|
3352
3352
|
title: "WorkOS hosted login: `workosAuthorizationUrl` → `workosBeginLogin`, with mandatory `state` + PKCE",
|
|
3353
3353
|
kind: "manual",
|
|
3354
3354
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
3355
3355
|
let t = e.getFullText();
|
|
3356
|
-
return t.includes("@voltro/plugin-auth-workos") &&
|
|
3356
|
+
return t.includes("@voltro/plugin-auth-workos") && zn.some((e) => t.includes(e));
|
|
3357
3357
|
}),
|
|
3358
3358
|
note: [
|
|
3359
3359
|
"YOUR WORKOS HOSTED-LOGIN FLOW HAD NO CSRF PROTECTION BY DEFAULT, AND NO PKCE.",
|
|
@@ -3411,12 +3411,12 @@ var x = (e, t, n) => {
|
|
|
3411
3411
|
"are accepted on the existing endpoints; your registered redirect URI is",
|
|
3412
3412
|
"unchanged."
|
|
3413
3413
|
].join("\n")
|
|
3414
|
-
}),
|
|
3414
|
+
}), Vn = /mirrorTables|_?voltro_mirror_/, Hn = C({
|
|
3415
3415
|
version: "0.34.0",
|
|
3416
3416
|
id: "0.34.0/05_analytics-mirror-versioned-writes",
|
|
3417
3417
|
title: "Analytics CDC-mirror: version-ordered writes, tombstoned deletes, retry + repair",
|
|
3418
3418
|
kind: "manual",
|
|
3419
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3419
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Vn.test(e.getFullText())),
|
|
3420
3420
|
note: [
|
|
3421
3421
|
"The analytics CDC-mirror (`mirrorTables`) now orders every write and",
|
|
3422
3422
|
"retries the ones that fail. Three things to do.",
|
|
@@ -3471,7 +3471,7 @@ var x = (e, t, n) => {
|
|
|
3471
3471
|
" VOLTRO_ANALYTICS_MIRROR_REPAIR_INTERVAL_MS (60000, 0 disables)",
|
|
3472
3472
|
" VOLTRO_ANALYTICS_MIRROR_REPAIR_QUEUE_LIMIT (10000)"
|
|
3473
3473
|
].join("\n")
|
|
3474
|
-
}),
|
|
3474
|
+
}), Un = C({
|
|
3475
3475
|
version: "0.34.0",
|
|
3476
3476
|
id: "0.34.0/06_origin-guard-and-trusted-proxies",
|
|
3477
3477
|
title: "Cross-site protection, trusted proxies and security headers are ON by default on the api listener",
|
|
@@ -3595,7 +3595,7 @@ var x = (e, t, n) => {
|
|
|
3595
3595
|
" a `Transfer-Encoding: chunked` body skipped it and was buffered without",
|
|
3596
3596
|
" bound. No action needed; VOLTRO_MAX_RPC_BODY_BYTES still tunes it."
|
|
3597
3597
|
].join("\n")
|
|
3598
|
-
}),
|
|
3598
|
+
}), Wn = C({
|
|
3599
3599
|
version: "0.34.0",
|
|
3600
3600
|
id: "0.34.0/07_incoming-webhook-declares-verification",
|
|
3601
3601
|
title: "An incoming webhook with no signature scheme now refuses to boot",
|
|
@@ -3644,7 +3644,7 @@ var x = (e, t, n) => {
|
|
|
3644
3644
|
"value — the sender holds the other half of it, so a generated one would",
|
|
3645
3645
|
"authenticate nobody."
|
|
3646
3646
|
].join("\n")
|
|
3647
|
-
}),
|
|
3647
|
+
}), Gn = C({
|
|
3648
3648
|
version: "0.34.0",
|
|
3649
3649
|
id: "0.34.0/08_init-initialises-the-workspace",
|
|
3650
3650
|
title: "`voltro init` initialises the workspace root; scaffolding is `create-project` only",
|
|
@@ -3696,20 +3696,20 @@ var x = (e, t, n) => {
|
|
|
3696
3696
|
"all 18 of its scripts — including the `pnpm db:up` the CLI then printed as a",
|
|
3697
3697
|
"next step. Run `voltro init` in the root first."
|
|
3698
3698
|
].join("\n")
|
|
3699
|
-
}),
|
|
3699
|
+
}), Kn = (e, t) => (e.getProperty(t)?.asKind(b.PropertyAssignment))?.getInitializer()?.asKind(b.ObjectLiteralExpression), qn = (e) => {
|
|
3700
3700
|
if (e.getExpression().getText() !== "workflow") return !1;
|
|
3701
3701
|
let t = e.getArguments()[0]?.asKind(b.ObjectLiteralExpression);
|
|
3702
3702
|
if (t === void 0) return !1;
|
|
3703
|
-
let n =
|
|
3703
|
+
let n = Kn(t, "messages");
|
|
3704
3704
|
return n !== void 0 && n.getProperty("queries") !== void 0;
|
|
3705
|
-
},
|
|
3705
|
+
}, Jn = C({
|
|
3706
3706
|
version: "0.34.0",
|
|
3707
3707
|
id: "0.34.0/09_workflow-messages-queries-removed",
|
|
3708
3708
|
title: "`workflow({ messages: { queries } })` is removed — it never had a send path",
|
|
3709
3709
|
kind: "manual",
|
|
3710
3710
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
3711
3711
|
let t = e.getFullText();
|
|
3712
|
-
return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(b.CallExpression).some(
|
|
3712
|
+
return !t.includes("queries") || !/workflow\s*\(/.test(t) ? !1 : e.getDescendantsOfKind(b.CallExpression).some(qn);
|
|
3713
3713
|
}),
|
|
3714
3714
|
note: [
|
|
3715
3715
|
"YOU DECLARED `messages: { queries: { … } }` ON A WORKFLOW. That channel is",
|
|
@@ -3752,7 +3752,7 @@ var x = (e, t, n) => {
|
|
|
3752
3752
|
"in-flight runs finish on the old path after a deploy. See the Versioning",
|
|
3753
3753
|
"page in the docs."
|
|
3754
3754
|
].join("\n")
|
|
3755
|
-
}),
|
|
3755
|
+
}), Yn = C({
|
|
3756
3756
|
version: "0.34.0",
|
|
3757
3757
|
id: "0.34.0/11_ai-flows-per-step-human-signal",
|
|
3758
3758
|
title: "`HUMAN_RESPONSE_SIGNAL` removed — a human step's signal name is now per-step",
|
|
@@ -3796,23 +3796,23 @@ var x = (e, t, n) => {
|
|
|
3796
3796
|
"per flow (`humanTimeoutMs`), per app (`aiFlowsPlugin({ humanReviewTimeoutMs })`)",
|
|
3797
3797
|
"or per deploy (`VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS`). `0` = wait forever."
|
|
3798
3798
|
].join("\n")
|
|
3799
|
-
}),
|
|
3799
|
+
}), Xn = /* @__PURE__ */ new Set([
|
|
3800
3800
|
"@voltro/plugin-auth",
|
|
3801
3801
|
"@voltro/protocol/session",
|
|
3802
3802
|
"@voltro/plugin-sso-saml"
|
|
3803
|
-
]),
|
|
3803
|
+
]), Zn = /* @__PURE__ */ new Set(["issueSession", "signSession"]), Qn = (e) => {
|
|
3804
3804
|
let t = e.getFullText();
|
|
3805
3805
|
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(b.CallExpression)) if (
|
|
3806
|
+
for (let t of e.getImportDeclarations()) if (Xn.has(t.getModuleSpecifierValue())) return !0;
|
|
3807
|
+
for (let t of e.getDescendantsOfKind(b.CallExpression)) if (Zn.has(t.getExpression().getText())) return !0;
|
|
3808
3808
|
for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
|
|
3809
3809
|
return !1;
|
|
3810
|
-
},
|
|
3810
|
+
}, $n = C({
|
|
3811
3811
|
version: "0.34.0",
|
|
3812
3812
|
id: "0.34.0/12_session-cookie-carries-identity",
|
|
3813
3813
|
title: "The session cookie carries identity only — authority resolves per request",
|
|
3814
3814
|
kind: "manual",
|
|
3815
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
3815
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(Qn),
|
|
3816
3816
|
note: [
|
|
3817
3817
|
"YOUR USERS WILL SIGN IN ONCE MORE, AND A SESSION COOKIE CAN NO LONGER",
|
|
3818
3818
|
"CARRY PERMISSIONS.",
|
|
@@ -3903,12 +3903,12 @@ var x = (e, t, n) => {
|
|
|
3903
3903
|
"not have: an authoritative resolver now overrides a token's own scope",
|
|
3904
3904
|
"claim, which union-only could never do."
|
|
3905
3905
|
].join("\n")
|
|
3906
|
-
}),
|
|
3906
|
+
}), er = /\bMockEmail\b|\.email\.(sent|lastTo|clear)\b|\bSentEmail\b/, tr = C({
|
|
3907
3907
|
version: "0.34.0",
|
|
3908
3908
|
id: "0.34.0/10_testing-mail-is-a-service",
|
|
3909
3909
|
title: "`ctx.email` / `MockEmail` removed — assert mail through the mail plugin",
|
|
3910
3910
|
kind: "manual",
|
|
3911
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3911
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => er.test(e.getFullText())),
|
|
3912
3912
|
note: [
|
|
3913
3913
|
"`@voltro/testing` no longer ships `MockEmail`, and `makeTestContext()` no",
|
|
3914
3914
|
"longer returns `ctx.email`. Your tests that touch it will not compile.",
|
|
@@ -3977,12 +3977,12 @@ var x = (e, t, n) => {
|
|
|
3977
3977
|
" • `makeTestContext({ layers })` — your app's own `app.config.ts`",
|
|
3978
3978
|
" `layers:` now resolve for an Effect-mode handler under test."
|
|
3979
3979
|
].join("\n")
|
|
3980
|
-
}),
|
|
3980
|
+
}), nr = /from\s+['"]@voltro\/plugin-billing(?:\/[\w-]+)?['"]/, rr = C({
|
|
3981
3981
|
version: "0.34.0",
|
|
3982
3982
|
id: "0.34.0/13_billing-provider-dunning",
|
|
3983
3983
|
title: "Billing dunning — `BillingProvider.fetchSubscription` is required, and `plan()` now honours a grace period",
|
|
3984
3984
|
kind: "manual",
|
|
3985
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
3985
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => nr.test(e.getFullText())),
|
|
3986
3986
|
note: [
|
|
3987
3987
|
"BILLING GAINED A DUNNING SEQUENCE. TWO THINGS CHANGED UNDER YOU.",
|
|
3988
3988
|
"",
|
|
@@ -4058,12 +4058,12 @@ var x = (e, t, n) => {
|
|
|
4058
4058
|
"`pastDueSince` / `statusEventAt` columns ride the declarative differ — they",
|
|
4059
4059
|
"are applied by `voltro db apply` and by a `voltro dev` boot, on every dialect."
|
|
4060
4060
|
].join("\n")
|
|
4061
|
-
}),
|
|
4061
|
+
}), ir = /from\s+['"]@voltro\/plugin-search(?:\/[\w-]+)?['"]/, ar = C({
|
|
4062
4062
|
version: "0.34.0",
|
|
4063
4063
|
id: "0.34.0/14_search-memory-backend-refuses-production",
|
|
4064
4064
|
title: "`plugin-search` on the in-memory backend now refuses to boot in production",
|
|
4065
4065
|
kind: "manual",
|
|
4066
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4066
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => ir.test(e.getFullText())),
|
|
4067
4067
|
note: [
|
|
4068
4068
|
"THE ZERO-CONFIG SEARCH BACKEND NO LONGER BOOTS IN PRODUCTION.",
|
|
4069
4069
|
"",
|
|
@@ -4132,7 +4132,7 @@ var x = (e, t, n) => {
|
|
|
4132
4132
|
"(`ts_headline` / `snippet()` / …) that `SearchQuery` promises. Degrading",
|
|
4133
4133
|
"those silently would replace one lie with another."
|
|
4134
4134
|
].join("\n")
|
|
4135
|
-
}),
|
|
4135
|
+
}), or = C({
|
|
4136
4136
|
version: "0.34.0",
|
|
4137
4137
|
id: "0.34.0/15_mysql-mssql-tls-honoured",
|
|
4138
4138
|
title: "mysql/mariadb and mssql connections honour ?ssl= / ?sslmode= — or refuse to boot",
|
|
@@ -4204,7 +4204,7 @@ var x = (e, t, n) => {
|
|
|
4204
4204
|
"spins in-tick and starves the event loop. Set it only if you know nothing in",
|
|
4205
4205
|
"your process does that, and set it well above their peak concurrency."
|
|
4206
4206
|
].join("\n")
|
|
4207
|
-
}),
|
|
4207
|
+
}), sr = C({
|
|
4208
4208
|
version: "0.34.0",
|
|
4209
4209
|
id: "0.34.0/16_declare-node-env-for-migration-commands",
|
|
4210
4210
|
title: "`voltro db apply` / `voltro migrate` now treat an unset NODE_ENV as production",
|
|
@@ -4268,12 +4268,12 @@ var x = (e, t, n) => {
|
|
|
4268
4268
|
"process environment if you want a non-production `voltro serve` without a",
|
|
4269
4269
|
"built bundle. Everything downstream of the launcher does honour `.env`."
|
|
4270
4270
|
].join("\n")
|
|
4271
|
-
}),
|
|
4271
|
+
}), cr = /\/(?:src\/)?pages\//, lr = /\bcacheInvalidatesOn\b/, ur = C({
|
|
4272
4272
|
version: "0.34.0",
|
|
4273
4273
|
id: "0.34.0/17_isr-postgres-refuses-without-a-database",
|
|
4274
4274
|
title: "The postgres ISR cache + CDC invalidator read DB_URL, and no longer fall back silently",
|
|
4275
4275
|
kind: "manual",
|
|
4276
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4276
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => lr.test(e.getFullText()) || cr.test(e.getFilePath())),
|
|
4277
4277
|
note: [
|
|
4278
4278
|
"YOUR WEB PROCESS MAY HAVE BEEN USING THE MEMORY ISR CACHE ALL ALONG.",
|
|
4279
4279
|
"",
|
|
@@ -4318,12 +4318,12 @@ var x = (e, t, n) => {
|
|
|
4318
4318
|
" until their `revalidate` window expires. Give the web process a `DB_URL`",
|
|
4319
4319
|
" or remove the declaration."
|
|
4320
4320
|
].join("\n")
|
|
4321
|
-
}),
|
|
4321
|
+
}), dr = /\.migration\.ts$/, fr = C({
|
|
4322
4322
|
version: "0.34.0",
|
|
4323
4323
|
id: "0.34.0/18_serve-refuses-pending-file-migrations",
|
|
4324
4324
|
title: "`voltro serve` refuses to boot with pending file-based migrations",
|
|
4325
4325
|
kind: "manual",
|
|
4326
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4326
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => dr.test(e.getFilePath())),
|
|
4327
4327
|
note: [
|
|
4328
4328
|
"YOUR DEPLOY MUST RUN `migrations/*.migration.ts` BEFORE `voltro serve`.",
|
|
4329
4329
|
"",
|
|
@@ -4366,11 +4366,11 @@ var x = (e, t, n) => {
|
|
|
4366
4366
|
"fingerprint check — it is the one switch for \"no boot-time schema checks\",",
|
|
4367
4367
|
"and it now covers both."
|
|
4368
4368
|
].join("\n")
|
|
4369
|
-
}),
|
|
4369
|
+
}), pr = /* @__PURE__ */ new Set([
|
|
4370
4370
|
"@voltro/plugin-auth",
|
|
4371
4371
|
"@voltro/plugin-auth/schema",
|
|
4372
4372
|
"@voltro/plugin-auth/plugin"
|
|
4373
|
-
]),
|
|
4373
|
+
]), mr = /* @__PURE__ */ new Set([
|
|
4374
4374
|
"UserStore",
|
|
4375
4375
|
"authTables",
|
|
4376
4376
|
"SendEmail",
|
|
@@ -4378,20 +4378,20 @@ var x = (e, t, n) => {
|
|
|
4378
4378
|
"memoryUserStore",
|
|
4379
4379
|
"postgresUserStore",
|
|
4380
4380
|
"authRoutesPlugin"
|
|
4381
|
-
]),
|
|
4381
|
+
]), hr = (e) => {
|
|
4382
4382
|
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 (
|
|
4383
|
+
for (let t of e.getImportDeclarations()) if (pr.has(t.getModuleSpecifierValue())) {
|
|
4384
|
+
for (let e of t.getNamedImports()) if (mr.has(e.getName())) return !0;
|
|
4385
4385
|
if (t.getNamespaceImport() !== void 0) return !0;
|
|
4386
4386
|
}
|
|
4387
4387
|
for (let t of e.getDescendantsOfKind(b.TypeReference)) if (t.getTypeName().getText() === "UserStore") return !0;
|
|
4388
4388
|
return !1;
|
|
4389
|
-
},
|
|
4389
|
+
}, gr = C({
|
|
4390
4390
|
version: "0.34.0",
|
|
4391
4391
|
id: "0.34.0/20_user-store-auth-feature-methods",
|
|
4392
4392
|
title: "UserStore gained the email-verification, invitation and impersonation methods",
|
|
4393
4393
|
kind: "manual",
|
|
4394
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4394
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(hr),
|
|
4395
4395
|
note: [
|
|
4396
4396
|
"IF YOU USE memoryUserStore OR postgresUserStore, THERE IS NOTHING TO DO.",
|
|
4397
4397
|
"Both already implement everything below. Read on only if you wrote your",
|
|
@@ -4471,26 +4471,26 @@ var x = (e, t, n) => {
|
|
|
4471
4471
|
" is the difference between \"new signups must confirm\" and \"everyone is",
|
|
4472
4472
|
" locked out until they check their mail\"."
|
|
4473
4473
|
].join("\n")
|
|
4474
|
-
}),
|
|
4474
|
+
}), _r = /* @__PURE__ */ new Set([
|
|
4475
4475
|
"bindConnectionSubject",
|
|
4476
4476
|
"getConnectionSubject",
|
|
4477
4477
|
"unbindConnectionSubject",
|
|
4478
4478
|
"onBindConnectionSubject",
|
|
4479
4479
|
"connectionSubjectsSnapshot"
|
|
4480
|
-
]),
|
|
4480
|
+
]), vr = (e) => {
|
|
4481
4481
|
let t = e.getFullText();
|
|
4482
4482
|
if (!/ConnectionSubject|connectionSubjectsSnapshot|rebind/.test(t)) return !1;
|
|
4483
|
-
for (let t of e.getDescendantsOfKind(b.CallExpression)) if (
|
|
4484
|
-
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (
|
|
4483
|
+
for (let t of e.getDescendantsOfKind(b.CallExpression)) if (_r.has(t.getExpression().getText())) return !0;
|
|
4484
|
+
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) if (_r.has(e.getName())) return !0;
|
|
4485
4485
|
for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "rebind") return !0;
|
|
4486
4486
|
for (let t of e.getDescendantsOfKind(b.ShorthandPropertyAssignment)) if (t.getName() === "rebind") return !0;
|
|
4487
4487
|
return !1;
|
|
4488
|
-
},
|
|
4488
|
+
}, yr = C({
|
|
4489
4489
|
version: "0.34.0",
|
|
4490
4490
|
id: "0.34.0/19_connection-override-carries-a-credential",
|
|
4491
4491
|
title: "Soft re-auth presents a credential — it no longer hands the server a Subject",
|
|
4492
4492
|
kind: "manual",
|
|
4493
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4493
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(vr),
|
|
4494
4494
|
note: [
|
|
4495
4495
|
"A LIVE CONNECTION CAN NO LONGER BE HANDED A SUBJECT. IT PRESENTS A",
|
|
4496
4496
|
"CREDENTIAL, AND THE SERVER RESOLVES IT LIKE ANY OTHER REQUEST.",
|
|
@@ -4575,19 +4575,19 @@ var x = (e, t, n) => {
|
|
|
4575
4575
|
"it behaves identically under `voltro dev` and `voltro serve`, because both",
|
|
4576
4576
|
"now build the middleware from one function (`makeAuthMiddlewareLayers`)."
|
|
4577
4577
|
].join("\n")
|
|
4578
|
-
}),
|
|
4578
|
+
}), br = (e) => {
|
|
4579
4579
|
if (/\.workflow(\.server)?\.tsx?$/.test(e.getBaseName())) return !0;
|
|
4580
4580
|
let t = e.getFullText();
|
|
4581
4581
|
if (!/resolveScopes|defineWorkflow|ctx\.workflows/.test(t)) return !1;
|
|
4582
4582
|
for (let t of e.getDescendantsOfKind(b.PropertyAssignment)) if (t.getName() === "resolveScopes") return !0;
|
|
4583
4583
|
for (let t of e.getDescendantsOfKind(b.CallExpression)) if (t.getExpression().getText() === "defineWorkflow") return !0;
|
|
4584
4584
|
return !1;
|
|
4585
|
-
},
|
|
4585
|
+
}, xr = C({
|
|
4586
4586
|
version: "0.34.0",
|
|
4587
4587
|
id: "0.34.0/20_workflow-authority-resolves-at-resume",
|
|
4588
4588
|
title: "A resumed workflow re-resolves its caller's authority instead of restoring it",
|
|
4589
4589
|
kind: "manual",
|
|
4590
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4590
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(br),
|
|
4591
4591
|
note: [
|
|
4592
4592
|
"A WORKFLOW NO LONGER CARRIES ITS CALLER'S PERMISSIONS FORWARD. IT CARRIES",
|
|
4593
4593
|
"THEIR IDENTITY, AND ASKS YOUR APP WHAT THEY MAY DO WHEN IT RUNS.",
|
|
@@ -4652,7 +4652,7 @@ var x = (e, t, n) => {
|
|
|
4652
4652
|
" contain scopes and are stripped on READ — a resumed run cannot re-assert",
|
|
4653
4653
|
" authority the old build persisted."
|
|
4654
4654
|
].join("\n")
|
|
4655
|
-
}),
|
|
4655
|
+
}), Sr = /* @__PURE__ */ new Set([
|
|
4656
4656
|
"listTag",
|
|
4657
4657
|
"createTag",
|
|
4658
4658
|
"updateTag",
|
|
@@ -4660,21 +4660,21 @@ var x = (e, t, n) => {
|
|
|
4660
4660
|
"createScope",
|
|
4661
4661
|
"writeScope",
|
|
4662
4662
|
"deleteScope"
|
|
4663
|
-
]),
|
|
4663
|
+
]), Cr = (e) => {
|
|
4664
4664
|
let t = e.getFullText();
|
|
4665
4665
|
if (!/EntityAdminSpec|deriveEntityAdmins|Tag\b|Scope\b/.test(t)) return !1;
|
|
4666
4666
|
for (let t of e.getImportDeclarations()) for (let e of t.getNamedImports()) {
|
|
4667
4667
|
let t = e.getName();
|
|
4668
4668
|
if (t === "EntityAdminSpec" || t === "deriveEntityAdmins") return !0;
|
|
4669
4669
|
}
|
|
4670
|
-
for (let t of e.getDescendantsOfKind(b.PropertyAccessExpression)) if (
|
|
4670
|
+
for (let t of e.getDescendantsOfKind(b.PropertyAccessExpression)) if (Sr.has(t.getName())) return !0;
|
|
4671
4671
|
return !1;
|
|
4672
|
-
},
|
|
4672
|
+
}, wr = C({
|
|
4673
4673
|
version: "0.34.0",
|
|
4674
4674
|
id: "0.34.0/21_admin-gates-on-declared-access",
|
|
4675
4675
|
title: "The derived admin gates on the api's DECLARED access — the invented `<table>:create` scopes are gone",
|
|
4676
4676
|
kind: "manual",
|
|
4677
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some(
|
|
4677
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some(Cr),
|
|
4678
4678
|
note: [
|
|
4679
4679
|
"YOUR ADMIN WAS GATING ON SCOPE STRINGS NOTHING PRODUCED.",
|
|
4680
4680
|
"",
|
|
@@ -4764,14 +4764,14 @@ var x = (e, t, n) => {
|
|
|
4764
4764
|
"from \"undeclared\", so an intentionally public action rendered as forbidden",
|
|
4765
4765
|
"to everybody."
|
|
4766
4766
|
].join("\n")
|
|
4767
|
-
}),
|
|
4767
|
+
}), Tr = /\b(useMutation|useAction|useWorkflow|useWorkflowSignal|useWorkflowUpdate|useUpload)\b/, Er = /FiberFailure|Cause\.squash|squashWith/, Dr = C({
|
|
4768
4768
|
version: "0.34.0",
|
|
4769
4769
|
id: "0.34.0/22_write-rejections-carry-the-typed-error",
|
|
4770
4770
|
title: "A rejected mutate/run/signal/upload carries the typed error, not a FiberFailure",
|
|
4771
4771
|
kind: "manual",
|
|
4772
4772
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4773
4773
|
let t = e.getFullText();
|
|
4774
|
-
return
|
|
4774
|
+
return Tr.test(t) || Er.test(t);
|
|
4775
4775
|
}),
|
|
4776
4776
|
note: [
|
|
4777
4777
|
"WHAT CHANGED. A write hook that rejects now rejects with the typed error",
|
|
@@ -4824,12 +4824,12 @@ var x = (e, t, n) => {
|
|
|
4824
4824
|
"contract, reject from a real rpc client, or assert on the value your",
|
|
4825
4825
|
"component branches on rather than on the literal the test wrote."
|
|
4826
4826
|
].join("\n")
|
|
4827
|
-
}),
|
|
4827
|
+
}), Or = /@voltro\/plugin-sso-saml|samlSsoPlugin/, kr = C({
|
|
4828
4828
|
version: "0.34.0",
|
|
4829
4829
|
id: "0.34.0/23_saml-signature-and-replay-defaults",
|
|
4830
4830
|
title: "SAML: response-signature requirement relaxed + surfaced; replay protection ON by default",
|
|
4831
4831
|
kind: "manual",
|
|
4832
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4832
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Or.test(e.getFullText())),
|
|
4833
4833
|
note: [
|
|
4834
4834
|
"Two defaults in `samlSsoPlugin` moved. Read both — they interact.",
|
|
4835
4835
|
"",
|
|
@@ -4920,24 +4920,24 @@ var x = (e, t, n) => {
|
|
|
4920
4920
|
"request, and the envelope is unauthenticated. If you must run unsolicited,",
|
|
4921
4921
|
"set `wantAuthnResponseSigned: true`."
|
|
4922
4922
|
].join("\n")
|
|
4923
|
-
}),
|
|
4923
|
+
}), Ar = /\b(signPayload|verifySignature)\s*\(/, jr = C({
|
|
4924
4924
|
version: "0.34.0",
|
|
4925
4925
|
id: "0.34.0/24_webhook-signing-renders-headers",
|
|
4926
4926
|
title: "Webhook signing: `signPayload`/`verifySignature` → `signRequest`/`verifyRequest`",
|
|
4927
4927
|
kind: "manual",
|
|
4928
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4928
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Ar.test(e.getFullText())),
|
|
4929
4929
|
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
|
-
}),
|
|
4930
|
+
}), Mr = /from\s+['"]@voltro\/plugin-presence(?:\/[a-z]+)?['"]/, Nr = /\b(lastSeen|presenceTable|memoryPresenceStore|isOnline|filterOnline|staleKeys|PresenceStore|PresenceEntry|PresenceTracker)\b/, Pr = /_voltro_presence/, Fr = C({
|
|
4931
4931
|
version: "0.34.0",
|
|
4932
4932
|
id: "0.34.0/25_presence-has-no-table",
|
|
4933
4933
|
title: "Presence: `_voltro_presence` is gone, and so is `lastSeen` on the roster",
|
|
4934
4934
|
kind: "manual",
|
|
4935
4935
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4936
4936
|
let t = e.getFullText();
|
|
4937
|
-
return
|
|
4937
|
+
return Pr.test(t) || Mr.test(t) && Nr.test(t);
|
|
4938
4938
|
}),
|
|
4939
4939
|
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
|
-
}),
|
|
4940
|
+
}), Ir = (e) => {
|
|
4941
4941
|
let t = e.getArguments()[0];
|
|
4942
4942
|
if (t === void 0 || !t.isKind(b.ObjectLiteralExpression)) return /* @__PURE__ */ new Set();
|
|
4943
4943
|
let n = /* @__PURE__ */ new Set();
|
|
@@ -4946,32 +4946,32 @@ var x = (e, t, n) => {
|
|
|
4946
4946
|
t !== void 0 && n.add(t);
|
|
4947
4947
|
}
|
|
4948
4948
|
return n;
|
|
4949
|
-
},
|
|
4949
|
+
}, Lr = (e, t) => /defineEvent\s*\(/.test(e) ? t.some((e) => {
|
|
4950
4950
|
if (e.getExpression().getText() !== "defineEvent") return !1;
|
|
4951
|
-
let t =
|
|
4951
|
+
let t = Ir(e);
|
|
4952
4952
|
return !t.has("guards") && !t.has("openAccess");
|
|
4953
|
-
}) : !1,
|
|
4953
|
+
}) : !1, Rr = C({
|
|
4954
4954
|
version: "0.35.0",
|
|
4955
4955
|
id: "0.35.0/01_event-access-decision",
|
|
4956
4956
|
title: "Every declared event decides who may listen (`guards:` or `openAccess:`)",
|
|
4957
4957
|
kind: "manual",
|
|
4958
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4958
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Lr(e.getFullText(), e.getDescendantsOfKind(b.CallExpression))),
|
|
4959
4959
|
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
|
-
}),
|
|
4960
|
+
}), zr = /\brouteHttp\s*\(/, Br = C({
|
|
4961
4961
|
version: "0.34.0",
|
|
4962
4962
|
id: "0.34.0/26_route-http-is-async",
|
|
4963
4963
|
title: "`routeHttp` returns a Promise",
|
|
4964
4964
|
kind: "manual",
|
|
4965
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
4965
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => zr.test(e.getFullText())),
|
|
4966
4966
|
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
|
-
}),
|
|
4967
|
+
}), Vr = /@voltro\/plugin-(billing|governance|storage)/, Hr = /* @__PURE__ */ RegExp("\\b(billing\\.(startCheckout|portalUrl|previewChange|changePlan|changeSeats|invoices|reportUsage)|governance\\.(export|erase)|storage\\.(mintUploadUrl|ingestUrl|listRefs))\\b"), Ur = /\bdefinePluginRoute\s*\(|\broutes\s*:\s*\[/, Wr = C({
|
|
4968
4968
|
version: "0.35.0",
|
|
4969
4969
|
id: "0.35.0/02_plugin-procedures-carry-access-decisions",
|
|
4970
4970
|
title: "First-party plugin procedures carry access decisions; twelve now require a scope",
|
|
4971
4971
|
kind: "manual",
|
|
4972
4972
|
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
4973
4973
|
let t = e.getFullText();
|
|
4974
|
-
return
|
|
4974
|
+
return Vr.test(t) || Hr.test(t) || Ur.test(t) && /VoltroPlugin|definePlugin/.test(t);
|
|
4975
4975
|
}),
|
|
4976
4976
|
note: [
|
|
4977
4977
|
"Every first-party plugin rpc route now declares an access decision, and the",
|
|
@@ -5036,12 +5036,12 @@ var x = (e, t, n) => {
|
|
|
5036
5036
|
"`openAccess` with the reason each is safe (self-scoped rows, signed",
|
|
5037
5037
|
"subject-bound tickets, per-object grants), and behave exactly as before."
|
|
5038
5038
|
].join("\n")
|
|
5039
|
-
}),
|
|
5039
|
+
}), Gr = /AnalyticsMirrorImpl/, Kr = C({
|
|
5040
5040
|
version: "0.35.0",
|
|
5041
5041
|
id: "0.35.0/03_analytics-mirror-max-version",
|
|
5042
5042
|
title: "Custom analytics mirror sinks answer `maxVersion` (fleet-scope seed read)",
|
|
5043
5043
|
kind: "manual",
|
|
5044
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5044
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => Gr.test(e.getFullText())),
|
|
5045
5045
|
note: [
|
|
5046
5046
|
"The analytics CDC-mirror now derives its version from the CHANGE under",
|
|
5047
5047
|
"`changeScope: 'fleet'` (postgres CDC, mysql binlog): warehouse baseline +",
|
|
@@ -5066,37 +5066,95 @@ var x = (e, t, n) => {
|
|
|
5066
5066
|
"",
|
|
5067
5067
|
"The read is issued at most once per key per process, off the commit path."
|
|
5068
5068
|
].join("\n")
|
|
5069
|
-
}),
|
|
5069
|
+
}), qr = (e) => /RunStepStatus/.test(e) || /['"]succeeded['"]/.test(e) && /['"]waiting['"]/.test(e) && /RunStep|runStep|flow/i.test(e), Jr = C({
|
|
5070
5070
|
version: "0.35.0",
|
|
5071
5071
|
id: "0.35.0/04_flow-run-step-skipped",
|
|
5072
5072
|
title: "A flow run step can now be 'skipped' — handle it where you render a run timeline",
|
|
5073
5073
|
kind: "manual",
|
|
5074
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5074
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => qr(e.getFullText())),
|
|
5075
5075
|
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
|
-
}),
|
|
5076
|
+
}), Yr = C({
|
|
5077
5077
|
version: "0.35.0",
|
|
5078
5078
|
id: "0.35.0/05_declared-schema-drops-runtime-flags",
|
|
5079
5079
|
title: "The declared schema no longer reads `CDC` / `VOLTRO_UNDO` / `VOLTRO_TRACING_PERSIST`",
|
|
5080
5080
|
kind: "manual",
|
|
5081
5081
|
appliesTo: () => !0,
|
|
5082
5082
|
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
|
-
}),
|
|
5083
|
+
}), G = "session_expired", Xr = C({
|
|
5084
5084
|
version: "0.35.1",
|
|
5085
5085
|
id: "0.35.1/01_unauthorized-replaces-session-expired",
|
|
5086
5086
|
title: "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
|
|
5087
5087
|
kind: "transform",
|
|
5088
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(
|
|
5088
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getFullText().includes(G)),
|
|
5089
5089
|
transform: ({ project: e, log: t }) => {
|
|
5090
5090
|
let n = [];
|
|
5091
5091
|
for (let t of e.getSourceFiles()) {
|
|
5092
|
-
if (!t.getFullText().includes(
|
|
5092
|
+
if (!t.getFullText().includes(G)) continue;
|
|
5093
5093
|
let e = !1;
|
|
5094
|
-
for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() ===
|
|
5094
|
+
for (let n of t.getDescendantsOfKind(11)) n.getLiteralValue() === G && (n.setLiteralValue("unauthorized"), e = !0);
|
|
5095
5095
|
e && n.push(t.getFilePath());
|
|
5096
5096
|
}
|
|
5097
|
-
n.length !== 0 && t(`rewrote '${
|
|
5097
|
+
n.length !== 0 && t(`rewrote '${G}' 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
5098
|
}
|
|
5099
|
-
}),
|
|
5099
|
+
}), Zr = /\(\s*\{[^}]*\.\.\./, Qr = /\bquery\??\.?\(\s*['"`]/, $r = C({
|
|
5100
|
+
version: "0.37.0",
|
|
5101
|
+
id: "0.37.0/01_procedure-input-rejects-undeclared-fields",
|
|
5102
|
+
title: "a procedure input rejects a field it does not declare (it used to drop it)",
|
|
5103
|
+
kind: "manual",
|
|
5104
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => {
|
|
5105
|
+
let t = e.getFullText();
|
|
5106
|
+
return Zr.test(t) || Qr.test(t) || w("query").test(t);
|
|
5107
|
+
}),
|
|
5108
|
+
note: [
|
|
5109
|
+
"A procedure call whose input carries a field the schema does not declare",
|
|
5110
|
+
"now fails with a ParseError naming the field. Before this release the",
|
|
5111
|
+
"field was DISCARDED and the call ran with what was left.",
|
|
5112
|
+
"",
|
|
5113
|
+
"The measurement that caused it — a consumer's root layout:",
|
|
5114
|
+
"",
|
|
5115
|
+
" query?.('userSettings.list', { employeeId })",
|
|
5116
|
+
"",
|
|
5117
|
+
"That procedure declares `userId` / `userIdIn`. The payload decoded to `{}`,",
|
|
5118
|
+
"which for a LIST query is not a narrower filter — it is NO filter. An admin",
|
|
5119
|
+
"was served a different user's settings row, and nothing logged anything.",
|
|
5120
|
+
"",
|
|
5121
|
+
"WHAT TO DO",
|
|
5122
|
+
"",
|
|
5123
|
+
"1. Run your test suite and your app against a dev build. A wrong field now",
|
|
5124
|
+
" surfaces as a ParseError that NAMES the key and lists the accepted ones,",
|
|
5125
|
+
" so each one is a one-line read rather than an investigation.",
|
|
5126
|
+
"",
|
|
5127
|
+
"2. Look at every SPREAD into a procedure input:",
|
|
5128
|
+
"",
|
|
5129
|
+
" await api.orders.list({ ...filters, limit })",
|
|
5130
|
+
"",
|
|
5131
|
+
" This is the construct the change is aimed at. The keys come from a value,",
|
|
5132
|
+
" so nobody reading the call site can see what is being sent — which is",
|
|
5133
|
+
" exactly the condition under which a stray key survived unnoticed.",
|
|
5134
|
+
"",
|
|
5135
|
+
"3. If a spread legitimately carries more than the procedure declares, narrow",
|
|
5136
|
+
" it at the call site (`{ status: filters.status, limit }`) rather than",
|
|
5137
|
+
" widening the input schema. Widening restores the silent drop under a",
|
|
5138
|
+
" different name: the field is accepted and still does nothing.",
|
|
5139
|
+
"",
|
|
5140
|
+
"WHAT DOES NOT CHANGE",
|
|
5141
|
+
"",
|
|
5142
|
+
" • A DECLARED field, including every optional one, decodes exactly as",
|
|
5143
|
+
" before. An empty input to a procedure that declares none is still fine.",
|
|
5144
|
+
" • Nested objects and union members follow the same rule — that is the",
|
|
5145
|
+
" point, since a filter object one level down is the commoner spelling.",
|
|
5146
|
+
" • A non-object input (a scalar, a void input) is unaffected; there is no",
|
|
5147
|
+
" excess property for it to have.",
|
|
5148
|
+
"",
|
|
5149
|
+
"WHY IT IS NOT A WARNING",
|
|
5150
|
+
"",
|
|
5151
|
+
"The decoder cannot tell a projection field from a FILTER field. Dropping an",
|
|
5152
|
+
"unknown `include` costs a caller some data; dropping an unknown `tenantId`",
|
|
5153
|
+
"hands them somebody else's. Since nothing at decode time distinguishes the",
|
|
5154
|
+
"two, the only safe direction is to refuse — and a warning would have to be",
|
|
5155
|
+
"read by someone, in a log, after the wrong rows were already served."
|
|
5156
|
+
].join("\n")
|
|
5157
|
+
}), K = "@voltro/plugin-auth", q = "@voltro/protocol", J = "IMPERSONATION_METADATA_KEY", ei = [
|
|
5100
5158
|
ge,
|
|
5101
5159
|
_e,
|
|
5102
5160
|
ye,
|
|
@@ -5157,104 +5215,159 @@ var x = (e, t, n) => {
|
|
|
5157
5215
|
un,
|
|
5158
5216
|
fn,
|
|
5159
5217
|
mn,
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
Dn,
|
|
5218
|
+
bn,
|
|
5219
|
+
Sn,
|
|
5220
|
+
wn,
|
|
5221
|
+
En,
|
|
5165
5222
|
On,
|
|
5166
5223
|
kn,
|
|
5167
5224
|
An,
|
|
5168
5225
|
jn,
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5226
|
+
Mn,
|
|
5227
|
+
Pn,
|
|
5228
|
+
Rn,
|
|
5229
|
+
Bn,
|
|
5173
5230
|
Hn,
|
|
5174
5231
|
Un,
|
|
5175
5232
|
Wn,
|
|
5176
|
-
|
|
5177
|
-
er,
|
|
5233
|
+
Gn,
|
|
5178
5234
|
Jn,
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5235
|
+
tr,
|
|
5236
|
+
Yn,
|
|
5237
|
+
$n,
|
|
5238
|
+
rr,
|
|
5182
5239
|
ar,
|
|
5183
5240
|
or,
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5241
|
+
sr,
|
|
5242
|
+
ur,
|
|
5243
|
+
fr,
|
|
5187
5244
|
yr,
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5245
|
+
xr,
|
|
5246
|
+
gr,
|
|
5247
|
+
wr,
|
|
5191
5248
|
Dr,
|
|
5192
5249
|
kr,
|
|
5193
|
-
|
|
5250
|
+
jr,
|
|
5251
|
+
Fr,
|
|
5252
|
+
Br,
|
|
5194
5253
|
Rr,
|
|
5195
|
-
Ir,
|
|
5196
|
-
Hr,
|
|
5197
5254
|
Wr,
|
|
5198
5255
|
Kr,
|
|
5199
|
-
qr,
|
|
5200
5256
|
Jr,
|
|
5257
|
+
Yr,
|
|
5258
|
+
Xr,
|
|
5259
|
+
$r,
|
|
5260
|
+
C({
|
|
5261
|
+
version: "0.38.0",
|
|
5262
|
+
id: "0.38.0/01_impersonation-key-in-protocol",
|
|
5263
|
+
title: `\`${J}\` is imported from @voltro/protocol`,
|
|
5264
|
+
kind: "transform",
|
|
5265
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => e.getImportDeclarations().some((e) => e.getModuleSpecifierValue() === K && e.getNamedImports().some((e) => e.getName() === J))),
|
|
5266
|
+
transform: ({ project: e, log: t }) => {
|
|
5267
|
+
let n = 0;
|
|
5268
|
+
for (let t of e.getSourceFiles()) for (let e of t.getImportDeclarations()) {
|
|
5269
|
+
if (e.getModuleSpecifierValue() !== K) continue;
|
|
5270
|
+
let r = e.getNamedImports().find((e) => e.getName() === J);
|
|
5271
|
+
if (r === void 0) continue;
|
|
5272
|
+
let i = r.getAliasNode()?.getText(), a = e.isTypeOnly() || r.isTypeOnly();
|
|
5273
|
+
r.remove();
|
|
5274
|
+
let o = t.getImportDeclarations().find((e) => e.getModuleSpecifierValue() === q && e.isTypeOnly() === a), s = {
|
|
5275
|
+
name: J,
|
|
5276
|
+
...i === void 0 ? {} : { alias: i }
|
|
5277
|
+
};
|
|
5278
|
+
o === void 0 ? t.addImportDeclaration({
|
|
5279
|
+
moduleSpecifier: q,
|
|
5280
|
+
isTypeOnly: a,
|
|
5281
|
+
namedImports: [s]
|
|
5282
|
+
}) : o.addNamedImport(s), e.getNamedImports().length === 0 && e.getDefaultImport() === void 0 && e.getNamespaceImport() === void 0 && e.remove(), n += 1;
|
|
5283
|
+
}
|
|
5284
|
+
n > 0 && t(`${J}: ${n} import(s) repointed to ${q}`);
|
|
5285
|
+
}
|
|
5286
|
+
}),
|
|
5201
5287
|
C({
|
|
5202
|
-
version: "0.
|
|
5203
|
-
id: "0.
|
|
5204
|
-
title: "a
|
|
5288
|
+
version: "0.38.0",
|
|
5289
|
+
id: "0.38.0/02_startup-failure-refuses-boot",
|
|
5290
|
+
title: "a `*.startup.ts` that fails refuses the boot (it used to warn and continue)",
|
|
5205
5291
|
kind: "manual",
|
|
5206
|
-
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) =>
|
|
5207
|
-
let t = e.getFullText();
|
|
5208
|
-
return Yr.test(t) || Xr.test(t) || w("query").test(t);
|
|
5209
|
-
}),
|
|
5292
|
+
appliesTo: ({ project: e }) => e.getSourceFiles().some((e) => /\.startup\.tsx?$/.test(e.getFilePath())),
|
|
5210
5293
|
note: [
|
|
5211
|
-
"A
|
|
5212
|
-
"
|
|
5213
|
-
"
|
|
5294
|
+
"A `*.startup.ts` whose function throws or rejects now ABORTS the boot,",
|
|
5295
|
+
"naming the file and the cause. Before this release it logged a warning and",
|
|
5296
|
+
"the server came up without whatever the startup was there to install.",
|
|
5297
|
+
"",
|
|
5298
|
+
"WHY IT CHANGED",
|
|
5214
5299
|
"",
|
|
5215
|
-
"
|
|
5300
|
+
"A startup is the documented place to REGISTER things the request path",
|
|
5301
|
+
"depends on — `setRowFilter` (row-level security) above all. We measured a",
|
|
5302
|
+
"startup that threw on its first line and produced this:",
|
|
5216
5303
|
"",
|
|
5217
|
-
"
|
|
5304
|
+
" warn startup: function rejected",
|
|
5305
|
+
" info startup: registered <- next line, same file",
|
|
5218
5306
|
"",
|
|
5219
|
-
"
|
|
5220
|
-
"
|
|
5221
|
-
"
|
|
5307
|
+
"and then a server that answered every request with no row filter",
|
|
5308
|
+
"registered. The two lines contradict each other and the second asserts",
|
|
5309
|
+
"exactly the thing that had just failed.",
|
|
5310
|
+
"",
|
|
5311
|
+
"The runner cannot tell a registration from a background loop, and the two",
|
|
5312
|
+
"outcomes are not symmetric: \"the app refuses to boot\" is fixed in seconds",
|
|
5313
|
+
"and everyone sees it; \"the app serves without its access control\" is seen",
|
|
5314
|
+
"by nobody.",
|
|
5222
5315
|
"",
|
|
5223
5316
|
"WHAT TO DO",
|
|
5224
5317
|
"",
|
|
5225
|
-
"1.
|
|
5226
|
-
"
|
|
5227
|
-
"
|
|
5318
|
+
"1. Boot each app once. A startup that was failing silently now says so, and",
|
|
5319
|
+
" the message names the file. This is the whole migration for most apps —",
|
|
5320
|
+
" a startup that worked keeps working.",
|
|
5321
|
+
"",
|
|
5322
|
+
"2. If a startup CAN fail and that is acceptable — a best-effort cache warm,",
|
|
5323
|
+
" an optional metrics bridge — catch it yourself:",
|
|
5324
|
+
"",
|
|
5325
|
+
" export default async (ctx) => {",
|
|
5326
|
+
" try {",
|
|
5327
|
+
" await warmTheCache(ctx.store)",
|
|
5328
|
+
" } catch (err) {",
|
|
5329
|
+
" ctx.log.warn('cache warm failed; serving cold', {}, err)",
|
|
5330
|
+
" }",
|
|
5331
|
+
" }",
|
|
5228
5332
|
"",
|
|
5229
|
-
"
|
|
5333
|
+
" The catch belongs at the site where somebody decided the failure was",
|
|
5334
|
+
" acceptable, where a reviewer can see the decision. That is the ONLY",
|
|
5335
|
+
" reason this is not a flag: a flag would move the decision away from the",
|
|
5336
|
+
" startup it applies to and make it one setting for all of them.",
|
|
5230
5337
|
"",
|
|
5231
|
-
"
|
|
5338
|
+
"TWO MORE WAYS A STARTUP USED TO VANISH, now also refusals",
|
|
5232
5339
|
"",
|
|
5233
|
-
"
|
|
5234
|
-
"
|
|
5235
|
-
"
|
|
5340
|
+
" • A startup file that cannot be IMPORTED (a bad import path, a syntax",
|
|
5341
|
+
" error) used to warn and skip the file.",
|
|
5342
|
+
" • A startup file with no DEFAULT-exported function used to warn and skip.",
|
|
5343
|
+
" The convention is `export default async (ctx) => {…}` — a named export",
|
|
5344
|
+
" is discovered and never runs, which is indistinguishable from a startup",
|
|
5345
|
+
" that ran and did nothing.",
|
|
5236
5346
|
"",
|
|
5237
|
-
"
|
|
5238
|
-
" it at the call site (`{ status: filters.status, limit }`) rather than",
|
|
5239
|
-
" widening the input schema. Widening restores the silent drop under a",
|
|
5240
|
-
" different name: the field is accepted and still does nothing.",
|
|
5347
|
+
"THE OTHER HALF: A STARTUP MUST RETURN",
|
|
5241
5348
|
"",
|
|
5242
|
-
"
|
|
5349
|
+
"A startup function that never returns now refuses the boot too, after",
|
|
5350
|
+
"`VOLTRO_STARTUP_TIMEOUT_MS` (60s default). Awaiting forever used to be",
|
|
5351
|
+
"allowed and documented, and it is what made a slow-then-failing startup",
|
|
5352
|
+
"unreportable: the boot moved on after 2 seconds, so a rejection arriving",
|
|
5353
|
+
"later had nothing left to refuse.",
|
|
5243
5354
|
"",
|
|
5244
|
-
"
|
|
5245
|
-
" before. An empty input to a procedure that declares none is still fine.",
|
|
5246
|
-
" • Nested objects and union members follow the same rule — that is the",
|
|
5247
|
-
" point, since a filter object one level down is the commoner spelling.",
|
|
5248
|
-
" • A non-object input (a scalar, a void input) is unaffected; there is no",
|
|
5249
|
-
" excess property for it to have.",
|
|
5355
|
+
"Start the work and RETURN; hand the teardown to `onShutdown`:",
|
|
5250
5356
|
"",
|
|
5251
|
-
"
|
|
5357
|
+
" export default async ({ store, log, onShutdown }) => {",
|
|
5358
|
+
" const timer = setInterval(() => sync(store), 60_000)",
|
|
5359
|
+
" onShutdown(() => clearInterval(timer))",
|
|
5360
|
+
" }",
|
|
5252
5361
|
"",
|
|
5253
|
-
"
|
|
5254
|
-
"
|
|
5255
|
-
"
|
|
5256
|
-
"
|
|
5257
|
-
"
|
|
5362
|
+
"Every startup this framework ships already has that shape, so most apps",
|
|
5363
|
+
"need no change. If yours awaits a fiber until shutdown, move the await",
|
|
5364
|
+
"behind the return.",
|
|
5365
|
+
"",
|
|
5366
|
+
"A startup that is legitimately SLOW is unaffected — it is waited for, and",
|
|
5367
|
+
"it registers when it finishes. Raise VOLTRO_STARTUP_TIMEOUT_MS if a real",
|
|
5368
|
+
"backfill needs more than a minute, or (better) register first and do the",
|
|
5369
|
+
"long work after, so a failure in the long half cannot hide a missing",
|
|
5370
|
+
"registration."
|
|
5258
5371
|
].join("\n")
|
|
5259
5372
|
})
|
|
5260
5373
|
], Y = /* @__PURE__ */ new Set([
|
|
@@ -5269,12 +5382,12 @@ var x = (e, t, n) => {
|
|
|
5269
5382
|
".turbo",
|
|
5270
5383
|
".cache",
|
|
5271
5384
|
".output"
|
|
5272
|
-
]),
|
|
5385
|
+
]), ti = () => [...[...Y].map((e) => `**/${e}/**`), "**/.voltro-*/**"], X = /(?:from|import)\s*\(?\s*['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g, ni = /\.(?:tsx?|jsx?|mjs|cjs)$/, ri = (e) => m(e).replace(ni, ""), ii = (e) => {
|
|
5273
5386
|
let t = e.split("/").filter((e) => e.length > 0).pop();
|
|
5274
5387
|
if (t === void 0 || t === "." || t === "..") return;
|
|
5275
|
-
let n = t.replace(
|
|
5388
|
+
let n = t.replace(ni, "");
|
|
5276
5389
|
return n.length > 0 ? n : void 0;
|
|
5277
|
-
},
|
|
5390
|
+
}, ai = (e) => {
|
|
5278
5391
|
let t = /* @__PURE__ */ new Map();
|
|
5279
5392
|
for (let n of e) {
|
|
5280
5393
|
let e;
|
|
@@ -5285,55 +5398,55 @@ var x = (e, t, n) => {
|
|
|
5285
5398
|
}
|
|
5286
5399
|
X.lastIndex = 0;
|
|
5287
5400
|
for (let r = X.exec(e); r !== null; r = X.exec(e)) {
|
|
5288
|
-
let e =
|
|
5401
|
+
let e = ii(r[1] ?? r[2] ?? "");
|
|
5289
5402
|
if (e === void 0) continue;
|
|
5290
5403
|
let i = t.get(e);
|
|
5291
5404
|
i === void 0 ? t.set(e, /* @__PURE__ */ new Set([n])) : i.add(n);
|
|
5292
5405
|
}
|
|
5293
5406
|
}
|
|
5294
5407
|
return t;
|
|
5295
|
-
},
|
|
5408
|
+
}, oi = (e, t) => {
|
|
5296
5409
|
let n = new Set(e);
|
|
5297
|
-
for (let r of e) for (let e of [
|
|
5410
|
+
for (let r of e) for (let e of [ri(r), m(h(r))]) {
|
|
5298
5411
|
let r = t.get(e);
|
|
5299
5412
|
if (r !== void 0) for (let e of r) n.add(e);
|
|
5300
5413
|
}
|
|
5301
5414
|
return n;
|
|
5302
|
-
},
|
|
5415
|
+
}, si = [
|
|
5303
5416
|
"**/*.ts",
|
|
5304
5417
|
"**/*.tsx",
|
|
5305
5418
|
"!**/node_modules/**",
|
|
5306
5419
|
"!**/dist/**",
|
|
5307
5420
|
"!**/.framework/**",
|
|
5308
5421
|
"!**/*.d.ts"
|
|
5309
|
-
],
|
|
5422
|
+
], Z = (e, t, n = ui) => {
|
|
5310
5423
|
let r = /* @__PURE__ */ new Set();
|
|
5311
|
-
for (let e of t) for (let t of e.scope ??
|
|
5424
|
+
for (let e of t) for (let t of e.scope ?? si) r.add(t);
|
|
5312
5425
|
let i = n(e);
|
|
5313
5426
|
if (i !== void 0) {
|
|
5314
|
-
let t = [...r].filter((e) => !e.startsWith("!")).map((e) =>
|
|
5427
|
+
let t = [...r].filter((e) => !e.startsWith("!")).map((e) => li(e)), n = [...r].filter((e) => e.startsWith("!")).map((e) => li(e.slice(1))), a = [...Y], o = [];
|
|
5315
5428
|
for (let r of i) {
|
|
5316
5429
|
if (a.some((e) => r === e || r.startsWith(`${e}/`) || r.includes(`/${e}/`)) || !t.some((e) => e(r)) || n.some((e) => e(r))) continue;
|
|
5317
5430
|
let i = g(e, r);
|
|
5318
5431
|
v(i) && o.push(i);
|
|
5319
5432
|
}
|
|
5320
|
-
return
|
|
5433
|
+
return ci(o.length, e), o;
|
|
5321
5434
|
}
|
|
5322
5435
|
let a = oe([...r], {
|
|
5323
5436
|
cwd: e,
|
|
5324
5437
|
absolute: !0,
|
|
5325
5438
|
expandDirectories: !1,
|
|
5326
5439
|
followSymbolicLinks: !1,
|
|
5327
|
-
ignore: [...
|
|
5440
|
+
ignore: [...ti()]
|
|
5328
5441
|
});
|
|
5329
|
-
return
|
|
5330
|
-
},
|
|
5331
|
-
let n =
|
|
5442
|
+
return ci(a.length, e), a;
|
|
5443
|
+
}, ci = (e, t) => {
|
|
5444
|
+
let n = di();
|
|
5332
5445
|
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.`);
|
|
5333
|
-
},
|
|
5446
|
+
}, li = (e) => {
|
|
5334
5447
|
let t = e.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "\0").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\u0000/g, "(?:.*/)?").replace(/\\\{([^}]*)\\\}/g, (e, t) => `(?:${t.split(",").join("|")})`);
|
|
5335
5448
|
return (e) => RegExp(`^${t}$`).test(e);
|
|
5336
|
-
},
|
|
5449
|
+
}, ui = (e) => {
|
|
5337
5450
|
try {
|
|
5338
5451
|
let t = re("git", [
|
|
5339
5452
|
"ls-files",
|
|
@@ -5354,17 +5467,17 @@ var x = (e, t, n) => {
|
|
|
5354
5467
|
} catch {
|
|
5355
5468
|
return;
|
|
5356
5469
|
}
|
|
5357
|
-
},
|
|
5470
|
+
}, di = () => {
|
|
5358
5471
|
let e = Number(process.env.VOLTRO_CODEMOD_MAX_FILES);
|
|
5359
5472
|
return Number.isInteger(e) && e > 0 ? e : 2e4;
|
|
5360
|
-
},
|
|
5473
|
+
}, Q = (e, t) => {
|
|
5361
5474
|
let n = e.split(".").map(Number), r = t.split(".").map(Number);
|
|
5362
5475
|
for (let e = 0; e < 3; e++) {
|
|
5363
5476
|
let t = (n[e] ?? 0) - (r[e] ?? 0);
|
|
5364
5477
|
if (t !== 0) return t;
|
|
5365
5478
|
}
|
|
5366
5479
|
return 0;
|
|
5367
|
-
},
|
|
5480
|
+
}, fi = (e, t, n = ei) => n.filter((n) => Q(e, n.version) < 0 && Q(n.version, t) <= 0).slice().sort(he), $ = (e, t, n = () => {}) => {
|
|
5368
5481
|
let r = [], i = [], a = [];
|
|
5369
5482
|
for (let o of t) {
|
|
5370
5483
|
if (!(!o.appliesTo || o.appliesTo({ project: e }))) {
|
|
@@ -5410,8 +5523,8 @@ var x = (e, t, n) => {
|
|
|
5410
5523
|
manual: i,
|
|
5411
5524
|
skipped: a
|
|
5412
5525
|
};
|
|
5413
|
-
},
|
|
5414
|
-
let t = e.log ?? (() => {}), n =
|
|
5526
|
+
}, pi = async (e) => {
|
|
5527
|
+
let t = e.log ?? (() => {}), n = fi(e.from, e.to, e.codemods ?? ei), r = e.only === void 0 || e.only.length === 0 ? n : (() => {
|
|
5415
5528
|
let t = n.filter((t) => e.only.includes(t.id)), r = e.only.filter((e) => !n.some((t) => t.id === e));
|
|
5416
5529
|
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")}`);
|
|
5417
5530
|
return t;
|
|
@@ -5430,15 +5543,15 @@ var x = (e, t, n) => {
|
|
|
5430
5543
|
baseUrl: i.baseUrl,
|
|
5431
5544
|
paths: i.paths
|
|
5432
5545
|
} }
|
|
5433
|
-
}), o, s =
|
|
5546
|
+
}), o, s = Z(e.root, r), c = r.filter((e) => e.needsImporters === !0 && e.scope !== void 0), l = [], u = [], d = [], p = c.length > 0 ? ai(Z(e.root, [{
|
|
5434
5547
|
...c[0],
|
|
5435
|
-
scope:
|
|
5548
|
+
scope: si
|
|
5436
5549
|
}])) : void 0;
|
|
5437
5550
|
for (let n of r) {
|
|
5438
5551
|
if (c.includes(n)) {
|
|
5439
|
-
let r = a(), i =
|
|
5440
|
-
for (let e of
|
|
5441
|
-
let s =
|
|
5552
|
+
let r = a(), i = Z(e.root, [n]);
|
|
5553
|
+
for (let e of oi(i, p)) v(e) && r.addSourceFileAtPath(e);
|
|
5554
|
+
let s = $(r, [n], t);
|
|
5442
5555
|
e.dryRun || await r.save(), l.push(...s.applied), u.push(...s.manual), d.push(...s.skipped), o = void 0;
|
|
5443
5556
|
continue;
|
|
5444
5557
|
}
|
|
@@ -5447,7 +5560,7 @@ var x = (e, t, n) => {
|
|
|
5447
5560
|
for (let t of s) v(t) && e.addSourceFileAtPath(t);
|
|
5448
5561
|
return e;
|
|
5449
5562
|
})();
|
|
5450
|
-
let r =
|
|
5563
|
+
let r = $(o, [n], t);
|
|
5451
5564
|
e.dryRun || await o.save(), l.push(...r.applied), u.push(...r.manual), d.push(...r.skipped);
|
|
5452
5565
|
}
|
|
5453
5566
|
return {
|
|
@@ -5457,4 +5570,4 @@ var x = (e, t, n) => {
|
|
|
5457
5570
|
};
|
|
5458
5571
|
};
|
|
5459
5572
|
//#endregion
|
|
5460
|
-
export { C as i,
|
|
5573
|
+
export { C as i, pi as n, Y as r, Q as t };
|