@vxil/cli 0.26.0 → 0.26.2
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/README.md +2 -1
- package/dist/_vxil-feature-configs.d.ts +1 -0
- package/dist/vxil-main.js +62 -17
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -23,7 +23,8 @@ export default defineConfig({
|
|
|
23
23
|
features: {
|
|
24
24
|
auth: { enabled: true },
|
|
25
25
|
cms: { enabled: true, draftPublish: true },
|
|
26
|
-
|
|
26
|
+
// provider 'resend' needs its key: `vxil secrets set RESEND_API_KEY` (or use provider: 'mock' to try it)
|
|
27
|
+
notifications: { enabled: true, provider: 'resend', resendApiKeyRef: 'secret:RESEND_API_KEY', fromEmail: 'noreply@example.com' },
|
|
27
28
|
},
|
|
28
29
|
// (b) CMS schema-as-code — collections + fields, reconciled by `vxil push`
|
|
29
30
|
cms: {
|
|
@@ -334,6 +334,7 @@ export declare const AuthConfigSchema: import("@sinclair/typebox").TObject<{
|
|
|
334
334
|
breachedPasswords: import("@sinclair/typebox").TBoolean;
|
|
335
335
|
captchaSecretRef: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
336
336
|
captchaOnReset: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
337
|
+
emailChangeStepUp: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
337
338
|
allowedRedirectOrigins: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
338
339
|
inviteOnly: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
339
340
|
verifyLinkEvicts: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
package/dist/vxil-main.js
CHANGED
|
@@ -4079,6 +4079,12 @@ var AuthConfigSchema = Type.Object({
|
|
|
4079
4079
|
// Optional, NO default (absent = false): every existing manifest folds
|
|
4080
4080
|
// byte-identically and reset stays ungated. Inert without captchaSecretRef.
|
|
4081
4081
|
captchaOnReset: Type.Optional(Type.Boolean()),
|
|
4082
|
+
// AUTH-EMAIL-CHANGE-GUARD (2026-10-13): when true, POST /email/change/request
|
|
4083
|
+
// answers 403 step_up_required unless THIS session passed a step-up in the
|
|
4084
|
+
// last 300 s (the password-change mechanism: auth.sessions.elevated_at).
|
|
4085
|
+
// Optional, NO default (absent = false) — existing manifests fold unchanged.
|
|
4086
|
+
// Requires otp.enabled (cross-field rule in validateFeatureConfig).
|
|
4087
|
+
emailChangeStepUp: Type.Optional(Type.Boolean()),
|
|
4082
4088
|
allowedRedirectOrigins: Type.Optional(Type.Array(
|
|
4083
4089
|
Type.String({ minLength: 8, maxLength: 253, pattern: REDIRECT_ORIGIN_PATTERN }),
|
|
4084
4090
|
{ maxItems: 32 }
|
|
@@ -5536,9 +5542,9 @@ function lowerAllTriggerBindings(def, name) {
|
|
|
5536
5542
|
return bindings;
|
|
5537
5543
|
}
|
|
5538
5544
|
var CONFIG_FILENAMES = ["vxil.config.ts", "vxil.config.mjs", "vxil.config.js"];
|
|
5539
|
-
var VXIL_CONFIG_PKG_VERSION = "0.19.
|
|
5540
|
-
var VXIL_SDK_PKG_VERSION = "0.26.
|
|
5541
|
-
var VXIL_CLI_PKG_VERSION = "0.26.
|
|
5545
|
+
var VXIL_CONFIG_PKG_VERSION = "0.19.1";
|
|
5546
|
+
var VXIL_SDK_PKG_VERSION = "0.26.1";
|
|
5547
|
+
var VXIL_CLI_PKG_VERSION = "0.26.2";
|
|
5542
5548
|
function ensureScaffoldPackageJson(cwd, opts = {}) {
|
|
5543
5549
|
const file = resolve(cwd, "package.json");
|
|
5544
5550
|
const wanted = {
|
|
@@ -10901,6 +10907,16 @@ function redirectOriginNotice(cfg) {
|
|
|
10901
10907
|
}
|
|
10902
10908
|
|
|
10903
10909
|
// src/link.ts
|
|
10910
|
+
function linkEnvLabel(o) {
|
|
10911
|
+
if (o.envFlag !== void 0) return { env: o.envFlag, fromWorkload: false };
|
|
10912
|
+
const wl = o.workload === "production" ? "production" : o.workload === "staging" ? "staging" : o.workload === "development" ? "dev" : void 0;
|
|
10913
|
+
if (wl) {
|
|
10914
|
+
if (o.kept !== void 0 && o.kept === "production" === (wl === "production")) return { env: o.kept, fromWorkload: false };
|
|
10915
|
+
const notice = o.kept !== void 0 ? `relabelled ${o.kept} \u2192 ${wl} from the project's workload; \`--env ${o.kept}\` to keep it` : void 0;
|
|
10916
|
+
return { env: wl, fromWorkload: true, ...notice ? { notice } : {} };
|
|
10917
|
+
}
|
|
10918
|
+
return { env: o.kept ?? envLabelFor(o.base), fromWorkload: false };
|
|
10919
|
+
}
|
|
10904
10920
|
function data(r) {
|
|
10905
10921
|
return r.json.data ?? r.json;
|
|
10906
10922
|
}
|
|
@@ -10934,7 +10950,7 @@ async function linkViaSession(dash, cookie, slug, keyName) {
|
|
|
10934
10950
|
}
|
|
10935
10951
|
const apiKey = data(mint).api_key;
|
|
10936
10952
|
if (!apiKey) throw new Error("mint returned no api_key");
|
|
10937
|
-
return { result: { slug, tenant_id: t.id, api_key: apiKey } };
|
|
10953
|
+
return { result: { slug, tenant_id: t.id, api_key: apiKey, ...typeof t.workload === "string" ? { workload: t.workload } : {} } };
|
|
10938
10954
|
}
|
|
10939
10955
|
async function linkViaDeviceFlow(dash, slug, io) {
|
|
10940
10956
|
const now = io.now ?? Date.now;
|
|
@@ -10960,7 +10976,7 @@ async function linkViaDeviceFlow(dash, slug, io) {
|
|
|
10960
10976
|
if (poll.status === 200) {
|
|
10961
10977
|
const p = data(poll);
|
|
10962
10978
|
if (!p.api_key || !p.tenant_id) throw new Error("approval returned no api_key");
|
|
10963
|
-
return { slug: p.slug || slug, tenant_id: p.tenant_id, api_key: p.api_key };
|
|
10979
|
+
return { slug: p.slug || slug, tenant_id: p.tenant_id, api_key: p.api_key, ...typeof p.workload === "string" ? { workload: p.workload } : {} };
|
|
10964
10980
|
}
|
|
10965
10981
|
throw new Error(`link failed (${errText(poll)})`);
|
|
10966
10982
|
}
|
|
@@ -11520,6 +11536,10 @@ function isWorkload(v) {
|
|
|
11520
11536
|
function workloadEnvLabel(w) {
|
|
11521
11537
|
return w === "production" ? "production" : w === "staging" ? "staging" : "dev";
|
|
11522
11538
|
}
|
|
11539
|
+
function projectCreateNextHint(slug, workload) {
|
|
11540
|
+
const env = isWorkload(workload) ? ` --env ${workloadEnvLabel(workload)}` : "";
|
|
11541
|
+
return `next: vxil link ${slug} --mint${env} && vxil push (this repo's binding is unchanged until then)`;
|
|
11542
|
+
}
|
|
11523
11543
|
function quickstartWorkload(workloadFlag, envFlag, dev) {
|
|
11524
11544
|
if (workloadFlag !== void 0) {
|
|
11525
11545
|
if (!isWorkload(workloadFlag)) return { error: `--workload must be one of ${PROJECT_WORKLOADS.join(", ")}` };
|
|
@@ -15523,6 +15543,17 @@ function formatCompared(c) {
|
|
|
15523
15543
|
}
|
|
15524
15544
|
return parts.join(" \xB7 ");
|
|
15525
15545
|
}
|
|
15546
|
+
function planFooter(counts, compared) {
|
|
15547
|
+
if (counts.drift > 0) return "(run `vxil push` to apply)";
|
|
15548
|
+
if (counts.ignored > 0 || counts.notCompared > 0) {
|
|
15549
|
+
const parts = [];
|
|
15550
|
+
if (counts.ignored > 0) parts.push(`${counts.ignored} ignored (\`vxil push\` would still apply them)`);
|
|
15551
|
+
if (counts.notCompared > 0) parts.push(`${counts.notCompared} not compared`);
|
|
15552
|
+
return `no unignored drift found \u2014 ${parts.join(" / ")}; not a clean match`;
|
|
15553
|
+
}
|
|
15554
|
+
const what = formatCompared(compared);
|
|
15555
|
+
return `\u2713 no drift \u2014 the target already matches your config${what ? ` (compared ${what})` : ""}; nothing to push`;
|
|
15556
|
+
}
|
|
15526
15557
|
|
|
15527
15558
|
// src/doctor.ts
|
|
15528
15559
|
var SECRETS_ADVISORY_NOTE = "advisory only: a config push with an unstored `secret:<name>` ref is a WARNING, never a 422 \u2014 vxil's promotion order sets production secrets AFTER the config push (`vxil secrets set <feature>/<name>`); doctor never deletes a stored secret";
|
|
@@ -16394,6 +16425,7 @@ async function reuseDevTenantBySlug(dash, cookie, slug) {
|
|
|
16394
16425
|
}
|
|
16395
16426
|
|
|
16396
16427
|
// src/try.ts
|
|
16428
|
+
var TRY_ENV_LABEL = "sandbox";
|
|
16397
16429
|
async function mintTry(fetchImpl, base2, opts = {}) {
|
|
16398
16430
|
const url = `${base2.replace(/\/$/, "")}/v1/try`;
|
|
16399
16431
|
const body = opts.turnstileToken ? { turnstile_token: opts.turnstileToken } : {};
|
|
@@ -22705,16 +22737,22 @@ async function dashFetch(base2, method, path, body, cookie) {
|
|
|
22705
22737
|
function makeDash(base2) {
|
|
22706
22738
|
return (method, path, body, cookie) => dashFetch(base2, method, path, body, cookie);
|
|
22707
22739
|
}
|
|
22708
|
-
function linkRequest(slug, tenantId, base2) {
|
|
22740
|
+
function linkRequest(slug, tenantId, base2, workload) {
|
|
22709
22741
|
const as = flag("as");
|
|
22710
|
-
const
|
|
22711
|
-
|
|
22742
|
+
const { env, fromWorkload, notice } = linkEnvLabel({
|
|
22743
|
+
base: base2,
|
|
22744
|
+
envFlag: flag("env"),
|
|
22745
|
+
kept: keptEnvLabel(loadProject(), slug, tenantId, as),
|
|
22746
|
+
workload
|
|
22747
|
+
});
|
|
22748
|
+
if (notice) console.error(`vxil: ${notice}`);
|
|
22749
|
+
return { slug, tenantId, edgeUrl: base2, env, fromWorkload, ...as !== void 0 ? { as } : {} };
|
|
22712
22750
|
}
|
|
22713
|
-
function persistLink(slug, tenantId, apiKey, base2) {
|
|
22714
|
-
const req = linkRequest(slug, tenantId, base2);
|
|
22751
|
+
function persistLink(slug, tenantId, apiKey, base2, workload) {
|
|
22752
|
+
const req = linkRequest(slug, tenantId, base2, workload);
|
|
22715
22753
|
const plan = planLinkBinding(loadProject(), req);
|
|
22716
22754
|
if ("error" in plan) fail(plan.error);
|
|
22717
|
-
const envWarn = envLabelWarning(req.env, base2);
|
|
22755
|
+
const envWarn = req.fromWorkload ? null : envLabelWarning(req.env, base2);
|
|
22718
22756
|
if (envWarn) console.error(`vxil: ${envWarn}`);
|
|
22719
22757
|
const creds = loadCredentials();
|
|
22720
22758
|
creds.keys = { ...creds.keys, [slug]: apiKey };
|
|
@@ -23168,7 +23206,7 @@ async function runApply(apply, sel = "prod", opts = {}) {
|
|
|
23168
23206
|
const gate = !apply && !!opts.gate;
|
|
23169
23207
|
const explain = !apply && !!opts.explain;
|
|
23170
23208
|
const { api, target } = requireApi(sel, apply ? { write: "push" } : gate ? { exitCode: 2 } : {});
|
|
23171
|
-
if (!apply) console.error(targetBanner(target, gate ? "diff" : "plan"));
|
|
23209
|
+
if (!apply) console.error(targetBanner(target, gate || opts.verb === "diff" ? "diff" : "plan"));
|
|
23172
23210
|
const report = { drift: 0, ignored: 0, errors: 0 };
|
|
23173
23211
|
const pushProblems = { hard: [], strict: [] };
|
|
23174
23212
|
const strictPush = apply && hasFlag("strict");
|
|
@@ -23221,8 +23259,10 @@ async function runApply(apply, sel = "prod", opts = {}) {
|
|
|
23221
23259
|
if (gate) report.errors++;
|
|
23222
23260
|
}
|
|
23223
23261
|
}
|
|
23262
|
+
let uncompared = 0;
|
|
23224
23263
|
const notCompared = (u, hint) => {
|
|
23225
23264
|
console.log(formatNotCompared(u, hint));
|
|
23265
|
+
uncompared++;
|
|
23226
23266
|
if (gate) report.errors++;
|
|
23227
23267
|
};
|
|
23228
23268
|
const runSection = async (label, fn) => {
|
|
@@ -23343,6 +23383,7 @@ ${r.feature}.${r.datum} (declared in config):`);
|
|
|
23343
23383
|
console.log(formatDeclaredApiState(r, { strict: strictApiState }));
|
|
23344
23384
|
if (r.skipped) continue;
|
|
23345
23385
|
if (r.refused) {
|
|
23386
|
+
uncompared++;
|
|
23346
23387
|
if (gate) report.errors++;
|
|
23347
23388
|
else console.error(`vxil: warning \u2014 ${r.feature}.${r.datum} not converged: ${r.refused}`);
|
|
23348
23389
|
if (apply) pushProblems.strict.push(`${r.feature}.${r.datum} refused: ${r.refused}`);
|
|
@@ -23350,6 +23391,7 @@ ${r.feature}.${r.datum} (declared in config):`);
|
|
|
23350
23391
|
}
|
|
23351
23392
|
if (r.deferred) {
|
|
23352
23393
|
console.error(`vxil: warning \u2014 ${r.feature}.${r.datum} not converged yet: ${r.deferred}`);
|
|
23394
|
+
uncompared++;
|
|
23353
23395
|
if (gate) report.errors++;
|
|
23354
23396
|
if (apply) pushProblems.strict.push(`${r.feature}.${r.datum} deferred: ${r.deferred}`);
|
|
23355
23397
|
continue;
|
|
@@ -23511,6 +23553,7 @@ api state \u2014 ${self.target.tenantSlug ?? "(env key)"} vs ${other.target.tena
|
|
|
23511
23553
|
for (const u of b2.unavailable) notCompared(u, `--against ${other.target.tenantSlug ?? ""}`.trim());
|
|
23512
23554
|
for (const w of [...truncationWarnings(a, "the target"), ...truncationWarnings(b2, "--against")]) {
|
|
23513
23555
|
console.log(w);
|
|
23556
|
+
uncompared++;
|
|
23514
23557
|
if (gate) report.errors++;
|
|
23515
23558
|
}
|
|
23516
23559
|
if (a.unavailable.length || b2.unavailable.length) return;
|
|
@@ -23556,7 +23599,8 @@ vxil diff \u2192 ${report.drift} drift item(s), ${report.ignored} ignored, ${rep
|
|
|
23556
23599
|
}
|
|
23557
23600
|
process.exit(code);
|
|
23558
23601
|
}
|
|
23559
|
-
if (!apply) console.log(
|
|
23602
|
+
if (!apply) console.log(`
|
|
23603
|
+
${planFooter({ drift: report.drift, ignored: report.ignored, notCompared: uncompared }, compared)}`);
|
|
23560
23604
|
if (apply && !hasFlag("no-gen")) await runGen(sel);
|
|
23561
23605
|
if (apply) {
|
|
23562
23606
|
const exitWith = pushExitCode(pushProblems, strictPush, target.slot === "primary" && target.tenantSlug ? { slug: target.tenantSlug } : {});
|
|
@@ -24346,7 +24390,7 @@ try {
|
|
|
24346
24390
|
tenant_id: result.tenant_id,
|
|
24347
24391
|
slug: result.slug,
|
|
24348
24392
|
edge_url: base2,
|
|
24349
|
-
env: flag("env") ??
|
|
24393
|
+
env: flag("env") ?? TRY_ENV_LABEL,
|
|
24350
24394
|
ephemeral: true,
|
|
24351
24395
|
...result.expires_at ? { expires_at: result.expires_at } : {}
|
|
24352
24396
|
});
|
|
@@ -24422,7 +24466,7 @@ try {
|
|
|
24422
24466
|
if (linkSession) {
|
|
24423
24467
|
const r = await linkViaSession(dash, linkSession, slug, keyName);
|
|
24424
24468
|
if ("result" in r) {
|
|
24425
|
-
const bound = persistLink(r.result.slug, r.result.tenant_id, r.result.api_key, base2);
|
|
24469
|
+
const bound = persistLink(r.result.slug, r.result.tenant_id, r.result.api_key, base2, r.result.workload);
|
|
24426
24470
|
console.log(`\u2713 linked '${slug}' ${bound} (minted a key via your dashboard session)`);
|
|
24427
24471
|
break;
|
|
24428
24472
|
}
|
|
@@ -24436,7 +24480,7 @@ try {
|
|
|
24436
24480
|
log: (l) => console.log(l),
|
|
24437
24481
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
24438
24482
|
});
|
|
24439
|
-
const bound = persistLink(linked.slug, linked.tenant_id, linked.api_key, base2);
|
|
24483
|
+
const bound = persistLink(linked.slug, linked.tenant_id, linked.api_key, base2, linked.workload);
|
|
24440
24484
|
console.log(`
|
|
24441
24485
|
\u2713 linked '${linked.slug}' ${bound} (approved in the browser)`);
|
|
24442
24486
|
}
|
|
@@ -24794,7 +24838,7 @@ try {
|
|
|
24794
24838
|
const { project } = await withDashSession(dash, base2, (c) => createProjectViaSession(dash, c, p.body));
|
|
24795
24839
|
if (jsonOut) console.log(JSON.stringify(project, null, 2));
|
|
24796
24840
|
else console.log(`\u2713 created '${project.slug}' (${project.display_name}, ${project.tier}${project.workload ? `, ${project.workload}` : ""}) \u2014 no key and no features yet`);
|
|
24797
|
-
console.error(
|
|
24841
|
+
console.error(projectCreateNextHint(project.slug, project.workload));
|
|
24798
24842
|
break;
|
|
24799
24843
|
}
|
|
24800
24844
|
if (sub === "workload") {
|
|
@@ -25049,6 +25093,7 @@ ${formatSummary(state)}`);
|
|
|
25049
25093
|
await runApply(false, sel, {
|
|
25050
25094
|
explain: hasFlag("explain"),
|
|
25051
25095
|
gate: against !== void 0 || hasFlag("exit-code"),
|
|
25096
|
+
verb: "diff",
|
|
25052
25097
|
...hasFlag("api-state") ? { apiState: { self: selector(), against: againstSelector(against) } } : {}
|
|
25053
25098
|
});
|
|
25054
25099
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxil/cli",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The vxil CLI — init, quickstart, push, gen, secrets, keys, migrate, doctor, diff, functions, payments; installs the `vxil` command (npm i -g @vxil/cli).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@vxil/config": "0.19.
|
|
49
|
-
"@vxil/feature-configs": "0.17.
|
|
48
|
+
"@vxil/config": "0.19.1",
|
|
49
|
+
"@vxil/feature-configs": "0.17.1",
|
|
50
50
|
"@vxil/runtime": "0.0.1",
|
|
51
51
|
"@vxil/types": "0.0.1",
|
|
52
52
|
"miniflare": "^4.20260730.0"
|