@rebasepro/cli 0.19.1 → 0.19.2-canary.g09316f6
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/dist/index.es.js +83 -36
- package/dist/index.es.js.map +1 -1
- package/dist/telemetry/consent.d.ts +25 -3
- package/package.json +7 -7
- package/templates/overlays/baas/backend/package.json +1 -1
- package/templates/overlays/baas/config/package.json +1 -1
- package/templates/overlays/baas/package.json +1 -1
- package/templates/template/backend/package.json +2 -1
- package/templates/template/config/package.json +1 -1
- package/templates/template/frontend/package.json +4 -4
- package/templates/template/frontend/vite.config.ts +5 -1
- package/templates/template/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -1474,33 +1474,12 @@ async function recordEvent(event, properties = {}, options = {}) {
|
|
|
1474
1474
|
}
|
|
1475
1475
|
//#endregion
|
|
1476
1476
|
//#region src/telemetry/consent.ts
|
|
1477
|
-
/**
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
* is the one where no consent exists yet. Asking before scaffolding puts a
|
|
1484
|
-
* privacy negotiation in front of someone who has not yet seen the tool do
|
|
1485
|
-
* anything — the worst possible moment, and a reliable way to get a reflexive
|
|
1486
|
-
* no.
|
|
1487
|
-
*
|
|
1488
|
-
* So the question is asked once the project exists and the user has seen it
|
|
1489
|
-
* work. The event's data is still in memory at that point, so nothing is lost
|
|
1490
|
-
* by waiting, and — this is the part that matters — **nothing has been
|
|
1491
|
-
* transmitted or written**. Declining leaves no id, no file, no record.
|
|
1492
|
-
*
|
|
1493
|
-
* ## Why the payload is shown rather than described
|
|
1494
|
-
*
|
|
1495
|
-
* "Anonymous usage data" is a phrase that has been used to mean almost
|
|
1496
|
-
* anything. Printing the exact JSON costs four lines of output and replaces a
|
|
1497
|
-
* claim the user has to take on faith with something they can read. It is also
|
|
1498
|
-
* the same builder the sender uses, so it cannot drift into a comfortable
|
|
1499
|
-
* fiction.
|
|
1500
|
-
*/
|
|
1501
|
-
/** True when we may ask: no decision recorded, and nothing else forbids it. */
|
|
1502
|
-
function shouldPrompt(env = process.env) {
|
|
1503
|
-
return suppressionReason(env) === "not_asked" && Boolean(process.stdin.isTTY);
|
|
1477
|
+
/** True when we may ask: nothing forbids it, and there is a question to ask. */
|
|
1478
|
+
function shouldPrompt(env = process.env, { reAskDeclined = false } = {}) {
|
|
1479
|
+
if (!process.stdin.isTTY) return false;
|
|
1480
|
+
const reason = suppressionReason(env);
|
|
1481
|
+
if (reason === "not_asked") return true;
|
|
1482
|
+
return reAskDeclined && reason === "declined";
|
|
1504
1483
|
}
|
|
1505
1484
|
function renderPreview(event, properties) {
|
|
1506
1485
|
const preview = buildEvent(event, properties, {
|
|
@@ -1516,8 +1495,9 @@ function renderPreview(event, properties) {
|
|
|
1516
1495
|
* CI must behave exactly as it does today, which means not asking and not
|
|
1517
1496
|
* sending.
|
|
1518
1497
|
*/
|
|
1519
|
-
async function promptForConsent(event, properties) {
|
|
1520
|
-
if (!shouldPrompt()) return false;
|
|
1498
|
+
async function promptForConsent(event, properties, options = {}) {
|
|
1499
|
+
if (!shouldPrompt(process.env, options)) return false;
|
|
1500
|
+
const askedBefore = suppressionReason(process.env) === "declined";
|
|
1521
1501
|
try {
|
|
1522
1502
|
console.log("");
|
|
1523
1503
|
console.log(chalk.bold("Help improve Rebase?"));
|
|
@@ -1531,6 +1511,11 @@ async function promptForConsent(event, properties) {
|
|
|
1531
1511
|
console.log("");
|
|
1532
1512
|
console.log(chalk.gray(" No project names, paths, schemas, URLs or error messages. Change your"));
|
|
1533
1513
|
console.log(chalk.gray(` mind any time with ${chalk.cyan("rebase telemetry disable")}.`));
|
|
1514
|
+
if (askedBefore) {
|
|
1515
|
+
console.log("");
|
|
1516
|
+
console.log(chalk.gray(" You declined before, and that is still the answer unless you change it"));
|
|
1517
|
+
console.log(chalk.gray(" here. Asked once per new project; never during ordinary work."));
|
|
1518
|
+
}
|
|
1534
1519
|
console.log("");
|
|
1535
1520
|
const { accepted } = await inquirer.prompt([{
|
|
1536
1521
|
type: "confirm",
|
|
@@ -1539,7 +1524,7 @@ async function promptForConsent(event, properties) {
|
|
|
1539
1524
|
default: false
|
|
1540
1525
|
}]);
|
|
1541
1526
|
setConsent(Boolean(accepted));
|
|
1542
|
-
console.log(accepted ? chalk.green(" Thank you — sharing enabled.") : chalk.gray(" Nothing will be sent. You will not be asked again."));
|
|
1527
|
+
console.log(accepted ? chalk.green(" Thank you — sharing enabled.") : chalk.gray(" Nothing will be sent." + (options.reAskDeclined ? " You will be asked again the next time you scaffold a project." : " You will not be asked again.")));
|
|
1543
1528
|
console.log("");
|
|
1544
1529
|
return Boolean(accepted);
|
|
1545
1530
|
} catch {
|
|
@@ -1653,7 +1638,7 @@ function buildInitQuestions(params) {
|
|
|
1653
1638
|
type: "input",
|
|
1654
1639
|
name: "projectName",
|
|
1655
1640
|
message: "Project name:",
|
|
1656
|
-
default: "my-
|
|
1641
|
+
default: "my-app",
|
|
1657
1642
|
validate: (input) => validateProjectName(input) ?? true
|
|
1658
1643
|
});
|
|
1659
1644
|
if (headlessArg === void 0) questions.push({
|
|
@@ -1721,7 +1706,7 @@ ${chalk.bold("Usage")}
|
|
|
1721
1706
|
rebase init ${chalk.blue("[name]")} [options]
|
|
1722
1707
|
|
|
1723
1708
|
${chalk.gray("The name may be a nested path (apps/my-app) or \".\" for the current directory.")}
|
|
1724
|
-
${chalk.gray("
|
|
1709
|
+
${chalk.gray("Asks when omitted; defaults to \"my-app\", and takes it without asking under --yes.")}
|
|
1725
1710
|
|
|
1726
1711
|
${chalk.bold("Options")}
|
|
1727
1712
|
${chalk.blue("-t, --template")} ${chalk.gray("<preset>")} blog | ecommerce | blank ${chalk.gray("(default: blog)")}
|
|
@@ -1803,7 +1788,7 @@ async function promptForOptions(rawArgs, pm) {
|
|
|
1803
1788
|
const installFlag = noInstall ? false : args["--install"] ?? false;
|
|
1804
1789
|
const hasInstallFlag = noInstall || args["--install"] === true;
|
|
1805
1790
|
if (isNonInteractive) {
|
|
1806
|
-
const projectName = nameArg || "my-
|
|
1791
|
+
const projectName = nameArg || "my-app";
|
|
1807
1792
|
const targetDirectory = path.resolve(process.cwd(), projectName);
|
|
1808
1793
|
const templateDirectory = path.resolve(cliRoot, "templates", "template");
|
|
1809
1794
|
const pmCommands = getPMCommands(pm);
|
|
@@ -2139,7 +2124,7 @@ async function createProject$1(options) {
|
|
|
2139
2124
|
git: Boolean(options.git),
|
|
2140
2125
|
duration: durationBucket(Date.now() - startedAt)
|
|
2141
2126
|
};
|
|
2142
|
-
if (await promptForConsent("cli.init", initProperties)) await recordEvent("cli.init", initProperties, { projectRoot: options.targetDirectory });
|
|
2127
|
+
if (await promptForConsent("cli.init", initProperties, { reAskDeclined: true })) await recordEvent("cli.init", initProperties, { projectRoot: options.targetDirectory });
|
|
2143
2128
|
}
|
|
2144
2129
|
/**
|
|
2145
2130
|
* Apply a template preset by replacing the default collection files.
|
|
@@ -5486,11 +5471,17 @@ function projectHasCollections(projectRoot) {
|
|
|
5486
5471
|
* Boot created all 30 tables through the additive ensure, `/health` answered
|
|
5487
5472
|
* 200, auth worked, the admin panel loaded — and every single
|
|
5488
5473
|
* `GET /api/data/*` returned a 500, `Table not found for collection 'posts'`,
|
|
5489
|
-
* because the driver
|
|
5490
|
-
* database. A stranger following the README had no way to guess that
|
|
5474
|
+
* because the driver looked the table up in the generated file rather than in
|
|
5475
|
+
* the database. A stranger following the README had no way to guess that
|
|
5491
5476
|
* `pnpm run schema:generate` was the missing step, and the README told them the
|
|
5492
5477
|
* schema was pushed for them.
|
|
5493
5478
|
*
|
|
5479
|
+
* That failure is fixed at the root — the driver builds its tables from
|
|
5480
|
+
* `information_schema` now, so a stub `schema.generated.ts` cannot empty the
|
|
5481
|
+
* API. This still runs, because the file is a real artifact for everything
|
|
5482
|
+
* around the server: `db push` diffs it, Atlas plans from it, `eject` writes it
|
|
5483
|
+
* out, and application code imports it.
|
|
5484
|
+
*
|
|
5494
5485
|
* ## Why it runs unconditionally
|
|
5495
5486
|
*
|
|
5496
5487
|
* Generation reads collection files and writes one file. It needs no database,
|
|
@@ -15168,7 +15159,31 @@ function resolveDeployArgs(rawArgs) {
|
|
|
15168
15159
|
appName: positionals[0]
|
|
15169
15160
|
};
|
|
15170
15161
|
}
|
|
15162
|
+
/**
|
|
15163
|
+
* `cli.deploy` — the event that says whether anyone ever ships.
|
|
15164
|
+
*
|
|
15165
|
+
* Declared in `TelemetryEventName` from the start and recorded nowhere, so a
|
|
15166
|
+
* consenting user reported scaffolding, dev servers and schema pushes and never
|
|
15167
|
+
* the one act the product exists for.
|
|
15168
|
+
*
|
|
15169
|
+
* What it carries is deliberately thin. No project id, no app name, no
|
|
15170
|
+
* deployment id, no URL: those are the "project names" and "URLs" the consent
|
|
15171
|
+
* screen promises are never sent, and a deployment id is a durable handle on
|
|
15172
|
+
* somebody's infrastructure. `frameworkVersion` is *our* version, not the
|
|
15173
|
+
* user's data, and it is the one field that makes the rest legible — a failure
|
|
15174
|
+
* rate means nothing without knowing which release it is a rate for.
|
|
15175
|
+
*/
|
|
15176
|
+
async function recordDeploy(o) {
|
|
15177
|
+
await recordEvent("cli.deploy", {
|
|
15178
|
+
followed: o.followed,
|
|
15179
|
+
status: o.status,
|
|
15180
|
+
deduplicated: o.deduplicated,
|
|
15181
|
+
framework_version: o.frameworkVersion ?? null,
|
|
15182
|
+
duration: durationBucket(Date.now() - o.startedAt)
|
|
15183
|
+
}, { projectRoot: process.cwd() });
|
|
15184
|
+
}
|
|
15171
15185
|
async function deployCommand(rawArgs, projectRef) {
|
|
15186
|
+
const startedAt = Date.now();
|
|
15172
15187
|
const { flags: args, appName } = resolveDeployArgs(rawArgs);
|
|
15173
15188
|
if (args["--wait"] && args["--no-follow"]) fail("--wait and --no-follow ask for opposite things.", "`deploy` follows by default — pass neither, or `--no-follow` to return as soon as the build is triggered.", "usage");
|
|
15174
15189
|
const { client, url } = await requireClient(rawArgs);
|
|
@@ -15252,6 +15267,13 @@ async function deployCommand(rawArgs, projectRef) {
|
|
|
15252
15267
|
following: false,
|
|
15253
15268
|
...warningPayload(warnings)
|
|
15254
15269
|
});
|
|
15270
|
+
await recordDeploy({
|
|
15271
|
+
followed: false,
|
|
15272
|
+
status: "not_followed",
|
|
15273
|
+
deduplicated,
|
|
15274
|
+
frameworkVersion,
|
|
15275
|
+
startedAt
|
|
15276
|
+
});
|
|
15255
15277
|
return;
|
|
15256
15278
|
}
|
|
15257
15279
|
if (!isJsonMode()) {
|
|
@@ -15264,6 +15286,13 @@ async function deployCommand(rawArgs, projectRef) {
|
|
|
15264
15286
|
projectId,
|
|
15265
15287
|
url
|
|
15266
15288
|
});
|
|
15289
|
+
await recordDeploy({
|
|
15290
|
+
followed: true,
|
|
15291
|
+
status,
|
|
15292
|
+
deduplicated,
|
|
15293
|
+
frameworkVersion,
|
|
15294
|
+
startedAt
|
|
15295
|
+
});
|
|
15267
15296
|
emit(() => {}, {
|
|
15268
15297
|
deploymentId,
|
|
15269
15298
|
deduplicated,
|
|
@@ -19073,6 +19102,24 @@ async function entry(args) {
|
|
|
19073
19102
|
return;
|
|
19074
19103
|
}
|
|
19075
19104
|
const effectiveSubcommand = parsedArgs["--help"] && !subcommand ? "--help" : subcommand;
|
|
19105
|
+
try {
|
|
19106
|
+
await dispatch(command, effectiveSubcommand, args, parsedArgs, namespacedCommands);
|
|
19107
|
+
} catch (error) {
|
|
19108
|
+
await recordEvent("cli.error", {
|
|
19109
|
+
command: command ?? "none",
|
|
19110
|
+
subcommand: effectiveSubcommand ?? "none",
|
|
19111
|
+
error_type: error instanceof Error ? error.constructor.name : "unknown",
|
|
19112
|
+
usage: Boolean(error && typeof error === "object" && error.isUsageError)
|
|
19113
|
+
}, { projectRoot: process.cwd() });
|
|
19114
|
+
throw error;
|
|
19115
|
+
}
|
|
19116
|
+
}
|
|
19117
|
+
/**
|
|
19118
|
+
* The dispatch, lifted out of {@link entry} so the whole of it sits inside one
|
|
19119
|
+
* `try`. Inlining the switch there would have put the `catch` around the flag
|
|
19120
|
+
* parsing too, which throws its own usage errors before a command is even named.
|
|
19121
|
+
*/
|
|
19122
|
+
async function dispatch(command, effectiveSubcommand, args, parsedArgs, namespacedCommands) {
|
|
19076
19123
|
switch (command) {
|
|
19077
19124
|
case "__dev-db-daemon": {
|
|
19078
19125
|
const { parseDaemonArgs, runDaemon } = await import("./daemon-entry-LTFKtpHy.js");
|