@rebasepro/cli 0.17.3 → 0.18.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/LICENSE +0 -1
- package/README.md +21 -3
- package/bin/rebase.js +93 -6
- package/dist/branch-pointer-dBiClJ0p.js +111 -0
- package/dist/branch-pointer-dBiClJ0p.js.map +1 -0
- package/dist/bundle.d.ts +80 -3
- package/dist/cli.d.ts +22 -0
- package/dist/commands/auth.d.ts +13 -0
- package/dist/commands/cloud/action-help.d.ts +0 -10
- package/dist/commands/cloud/auth.d.ts +37 -0
- package/dist/commands/cloud/context.d.ts +90 -2
- package/dist/commands/cloud/databases.d.ts +0 -2
- package/dist/commands/cloud/declared-resources.d.ts +3 -0
- package/dist/commands/cloud/deploy.d.ts +76 -6
- package/dist/commands/cloud/deployments.d.ts +9 -2
- package/dist/commands/cloud/index.d.ts +36 -0
- package/dist/commands/cloud/link.d.ts +1 -1
- package/dist/commands/cloud/orgs.d.ts +6 -0
- package/dist/commands/cloud/resources.d.ts +81 -3
- package/dist/commands/cloud/settings.d.ts +7 -0
- package/dist/commands/db.d.ts +119 -9
- package/dist/commands/dev.d.ts +216 -1
- package/dist/commands/doctor.d.ts +51 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/skills.d.ts +82 -13
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/telemetry.d.ts +0 -8
- package/dist/{daemon-Bdl4lrdt.js → daemon-YDZK9NZ1.js} +88 -9
- package/dist/daemon-YDZK9NZ1.js.map +1 -0
- package/dist/{daemon-entry-CmJn83zu.js → daemon-entry-LTFKtpHy.js} +117 -44
- package/dist/daemon-entry-LTFKtpHy.js.map +1 -0
- package/dist/dev-db/branch-pointer.d.ts +71 -0
- package/dist/dev-db/daemon.d.ts +27 -0
- package/dist/dev-db/prepare.d.ts +62 -0
- package/dist/dev-db/pull.d.ts +28 -0
- package/dist/dev-db/resolve.d.ts +45 -6
- package/dist/dev-db/state.d.ts +21 -0
- package/dist/dev-preflight-CNLl4rdo.js +367 -0
- package/dist/dev-preflight-CNLl4rdo.js.map +1 -0
- package/dist/doctor-environment.d.ts +131 -0
- package/dist/function-portability.d.ts +7 -0
- package/dist/index.d.ts +25 -15
- package/dist/index.es.js +5594 -1741
- package/dist/index.es.js.map +1 -1
- package/dist/project-3WCbrrfW.js +499 -0
- package/dist/project-3WCbrrfW.js.map +1 -0
- package/dist/{pull-DqPRu1te.js → pull-Bj0XnH5s.js} +31 -2
- package/dist/pull-Bj0XnH5s.js.map +1 -0
- package/dist/resolve-Y56osuQH.js +74 -0
- package/dist/resolve-Y56osuQH.js.map +1 -0
- package/dist/resources/derive.d.ts +67 -1
- package/dist/resources/status.d.ts +128 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/{state-c0CJ6Kwb.js → state-C59Elrnt.js} +24 -15
- package/dist/state-C59Elrnt.js.map +1 -0
- package/dist/utils/args.d.ts +18 -0
- package/dist/utils/command-words.d.ts +45 -0
- package/dist/utils/dev-preflight.d.ts +46 -0
- package/dist/utils/package-manager.d.ts +23 -0
- package/dist/utils/project.d.ts +66 -0
- package/dist/utils/spawn-error.d.ts +1 -0
- package/dist/utils/unknown-command.d.ts +26 -0
- package/dist/utils/version.d.ts +10 -0
- package/package.json +36 -28
- package/templates/eject/backend/src/env.ts +9 -0
- package/templates/eject/backend/src/index.ts +46 -19
- package/templates/eject/config/resources.ts +1 -1
- package/templates/overlays/baas/README.md +31 -11
- package/templates/overlays/baas/backend/package.json +2 -2
- package/templates/overlays/baas/backend/tsconfig.json +1 -1
- package/templates/overlays/baas/package.json +5 -2
- package/templates/overlays/baas/pnpm-workspace.yaml +13 -0
- package/templates/template/.cursorrules +1 -0
- package/templates/template/.env.example +91 -8
- package/templates/template/.github/copilot-instructions.md +1 -0
- package/templates/template/.mcp.json +11 -0
- package/templates/template/.windsurfrules +1 -0
- package/templates/template/AGENTS.md +1 -0
- package/templates/template/CLAUDE.md +2 -1
- package/templates/template/README.md +59 -28
- package/templates/template/ai-instructions.md +29 -12
- package/templates/template/backend/functions/hello.ts +5 -1
- package/templates/template/backend/package.json +2 -2
- package/templates/template/backend/src/schema.generated.ts +8 -3
- package/templates/template/backend/tsconfig.json +1 -1
- package/templates/template/config/cms.d.ts +6 -0
- package/templates/template/config/package.json +1 -1
- package/templates/template/config/resources.ts +42 -3
- package/templates/template/config/storage.ts +2 -1
- package/templates/template/docker-compose.yml +50 -9
- package/templates/template/frontend/index.html +0 -1
- package/templates/template/frontend/package.json +1 -2
- package/templates/template/frontend/src/App.tsx +8 -2
- package/templates/template/frontend/vite.config.ts +8 -0
- package/templates/template/gitignore +19 -1
- package/templates/template/npmrc +13 -4
- package/templates/template/package.json +10 -1
- package/templates/template/pnpm-workspace.yaml +17 -0
- package/templates/template/scripts/example.ts +4 -1
- package/dist/daemon-Bdl4lrdt.js.map +0 -1
- package/dist/daemon-entry-CmJn83zu.js.map +0 -1
- package/dist/pull-DqPRu1te.js.map +0 -1
- package/dist/state-c0CJ6Kwb.js.map +0 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ Developer CLI for scaffolding, running, and managing Rebase projects.
|
|
|
8
8
|
pnpm add -g @rebasepro/cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
ESM-only: `"type": "module"` with no CommonJS build, so it is loaded with
|
|
12
|
+
`import`. `require()` of it resolves only on Node 22.12+, which supports
|
|
13
|
+
`require(esm)`.
|
|
14
|
+
|
|
11
15
|
The CLI is also bundled with every Rebase project as a local dependency.
|
|
12
16
|
|
|
13
17
|
## Commands
|
|
@@ -26,6 +30,9 @@ The CLI is also bundled with every Rebase project as a local dependency.
|
|
|
26
30
|
| `rebase generate-sdk` | Generate a typed TypeScript SDK from collections |
|
|
27
31
|
| `rebase auth reset-password` | Reset a user's password |
|
|
28
32
|
| `rebase doctor` | Detect schema drift between collections, Drizzle schema, and database |
|
|
33
|
+
| `rebase status` | Show every resource this project declares and whether its variables are set |
|
|
34
|
+
| `rebase resources` | List the databases, buckets and topics this project declares |
|
|
35
|
+
| `rebase skills install` | Install Rebase agent skills for your AI coding assistant |
|
|
29
36
|
| `rebase cloud <command>` | Manage your apps on Rebase Cloud (auth, deploy, databases, …) |
|
|
30
37
|
|
|
31
38
|
Run `rebase --help` or `rebase <command> --help` for detailed usage.
|
|
@@ -61,13 +68,24 @@ Run `rebase cloud --help` for the full list.
|
|
|
61
68
|
## Quick Start
|
|
62
69
|
|
|
63
70
|
```bash
|
|
64
|
-
|
|
71
|
+
pnpm dlx @rebasepro/cli init my-app
|
|
65
72
|
cd my-app
|
|
66
|
-
|
|
67
|
-
pnpm run db:push
|
|
73
|
+
pnpm install
|
|
68
74
|
pnpm run dev
|
|
69
75
|
```
|
|
70
76
|
|
|
77
|
+
That is the whole first run. With no `DATABASE_URL` set, `rebase dev` starts a
|
|
78
|
+
managed PostgreSQL (PGlite) in the project directory, generates the Drizzle
|
|
79
|
+
schema from your collections, and creates the tables at boot — no database to
|
|
80
|
+
install and no schema step.
|
|
81
|
+
|
|
82
|
+
Two commands worth knowing straight after:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
rebase status # every resource, and whether its variables are set
|
|
86
|
+
rebase skills install # Rebase skills for your AI coding assistant
|
|
87
|
+
```
|
|
88
|
+
|
|
71
89
|
## Related Packages
|
|
72
90
|
|
|
73
91
|
| Package | Role |
|
package/bin/rebase.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
@@ -7,6 +7,85 @@ const here = dirname(fileURLToPath(import.meta.url));
|
|
|
7
7
|
const distEntry = join(here, "..", "dist", "index.es.js");
|
|
8
8
|
const srcDir = join(here, "..", "src");
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Colour, but only for a terminal.
|
|
12
|
+
*
|
|
13
|
+
* Every other line the CLI prints goes through chalk, which checks this for
|
|
14
|
+
* itself. These three did not — they are written before the bundle is even
|
|
15
|
+
* imported, so they hard-coded `\x1b[31m` — and stderr is exactly where that
|
|
16
|
+
* costs something: `rebase status extra 2>err.txt` wrote the escapes into the
|
|
17
|
+
* file, and CI logs, `2>&1 | grep`, and every agent reading a failed command's
|
|
18
|
+
* output got them too.
|
|
19
|
+
*
|
|
20
|
+
* `NO_COLOR` and `FORCE_COLOR` are the two conventions chalk honours, so
|
|
21
|
+
* honouring the same two keeps one CLI rather than two.
|
|
22
|
+
*/
|
|
23
|
+
const useColor = process.env.FORCE_COLOR !== undefined && process.env.FORCE_COLOR !== "0"
|
|
24
|
+
? true
|
|
25
|
+
: Boolean(process.stderr.isTTY) && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
26
|
+
|
|
27
|
+
const paint = (code, text) => (useColor ? `\x1b[${code}m${text}\x1b[0m` : text);
|
|
28
|
+
const red = (text) => paint(31, text);
|
|
29
|
+
const yellow = (text) => paint(33, text);
|
|
30
|
+
const dim = (text) => paint(90, text);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Refuse to run on a Node older than this CLI's own floor, before anything else.
|
|
34
|
+
*
|
|
35
|
+
* `engines.node` is declared on all 22 packages and `check:floors` keeps the
|
|
36
|
+
* numbers in step — on the reasoning that "npm and pnpm check `engines` on
|
|
37
|
+
* install, so the number is load-bearing". pnpm 11 does not: a project whose
|
|
38
|
+
* `engines.node` is `>=99.0.0` installs silently and exits 0, and so does a
|
|
39
|
+
* *dependency* declaring it. npm prints `EBADENGINE` and also exits 0. So the
|
|
40
|
+
* declared floor was enforced by nothing at all, and `rebase init` on Node 20
|
|
41
|
+
* said nothing until a syntax error surfaced from inside a dependency —
|
|
42
|
+
* `checkNodeVersion` existed but only `rebase doctor` ever called it.
|
|
43
|
+
*
|
|
44
|
+
* First, and before `await import("../dist/…")`, because the bundle is the
|
|
45
|
+
* thing most likely to fail incomprehensibly on an old runtime: the point is to
|
|
46
|
+
* name the version rather than let the reader debug a stack trace in minified
|
|
47
|
+
* output.
|
|
48
|
+
*
|
|
49
|
+
* The floor is read from this package's own `package.json` rather than written
|
|
50
|
+
* here, so it cannot drift from what `check:floors` gates and from what
|
|
51
|
+
* `rebase doctor` reports. The comparison duplicates
|
|
52
|
+
* `doctor-environment.ts`'s — importing it would mean importing the bundle,
|
|
53
|
+
* which is the thing being guarded — and `bin-floor.test.ts` asserts the two
|
|
54
|
+
* still agree.
|
|
55
|
+
*/
|
|
56
|
+
function assertNodeFloor() {
|
|
57
|
+
let required;
|
|
58
|
+
try {
|
|
59
|
+
const manifest = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
|
|
60
|
+
required = manifest?.engines?.node;
|
|
61
|
+
} catch {
|
|
62
|
+
return; // No manifest, no floor to enforce. Never a reason to refuse.
|
|
63
|
+
}
|
|
64
|
+
if (typeof required !== "string") return;
|
|
65
|
+
|
|
66
|
+
// `>=x[.y[.z]]`, the only form any engines.node in this repo takes. Anything
|
|
67
|
+
// else parses to nothing and the guard stands down rather than guessing.
|
|
68
|
+
const declared = required.match(/>=\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
69
|
+
if (!declared) return;
|
|
70
|
+
const running = process.versions.node.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
|
71
|
+
if (!running) return;
|
|
72
|
+
|
|
73
|
+
for (let i = 1; i <= 3; i++) {
|
|
74
|
+
const min = Number(declared[i] ?? 0);
|
|
75
|
+
const now = Number(running[i] ?? 0);
|
|
76
|
+
if (now > min) return;
|
|
77
|
+
if (now < min) {
|
|
78
|
+
process.stderr.write(
|
|
79
|
+
`${red(`✗ rebase CLI: Node ${process.versions.node} is running, and Rebase needs ${required}.`)}\n` +
|
|
80
|
+
` Install Node ${declared[1]} or newer (nvm install ${declared[1]}), then reinstall dependencies.\n`
|
|
81
|
+
);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
assertNodeFloor();
|
|
88
|
+
|
|
10
89
|
/**
|
|
11
90
|
* Warn when the built CLI is older than the source it was built from.
|
|
12
91
|
*
|
|
@@ -53,7 +132,7 @@ function warnIfStale() {
|
|
|
53
132
|
? `${Math.round(seconds / 3600)}h`
|
|
54
133
|
: seconds >= 60 ? `${Math.round(seconds / 60)}m` : `${seconds}s`;
|
|
55
134
|
process.stderr.write(
|
|
56
|
-
|
|
135
|
+
`${yellow(`⚠ rebase CLI: dist/ is ${ago} older than src/ — you are running a stale build.`)}\n` +
|
|
57
136
|
` Rebuild with: (cd ${join(here, "..")} && npm run build)\n`
|
|
58
137
|
);
|
|
59
138
|
}
|
|
@@ -77,7 +156,7 @@ try {
|
|
|
77
156
|
if (!existsSync(distEntry)) {
|
|
78
157
|
const dev = existsSync(srcDir);
|
|
79
158
|
process.stderr.write(
|
|
80
|
-
|
|
159
|
+
`${red(`✗ rebase CLI: not built yet — ${distEntry} is missing.`)}\n` +
|
|
81
160
|
(dev
|
|
82
161
|
? " Build it with: pnpm --filter @rebasepro/cli build\n" +
|
|
83
162
|
" (or `pnpm build` from the repo root to build every package)\n"
|
|
@@ -102,16 +181,24 @@ const { entry } = await import("../dist/index.es.js");
|
|
|
102
181
|
* friendly and exit never reach here. The stack is available behind
|
|
103
182
|
* `--debug`/`REBASE_DEBUG`, because when the message is *not* enough that is
|
|
104
183
|
* the only thing that helps.
|
|
184
|
+
*
|
|
185
|
+
* With one exception: a *usage* error. `rebase status extra` has no stack worth
|
|
186
|
+
* reading — it points at `arg` and `utils/args.ts` — and the hint suggests
|
|
187
|
+
* re-running with another flag, when the flags are precisely what went wrong.
|
|
188
|
+
* `utils/args.ts` marks those with `isUsageError` rather than a class, because
|
|
189
|
+
* this file imports the bundle and `instanceof` cannot reach across it.
|
|
105
190
|
*/
|
|
106
191
|
const wantsStack = process.argv.includes("--debug") || process.env.REBASE_DEBUG === "1";
|
|
107
192
|
|
|
108
193
|
entry(process.argv).catch((error) => {
|
|
109
194
|
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
-
|
|
195
|
+
const isUsage = Boolean(error && typeof error === "object" && error.isUsageError);
|
|
196
|
+
|
|
197
|
+
process.stderr.write(`${red(`✗ ${message}`)}\n`);
|
|
111
198
|
if (wantsStack && error instanceof Error && error.stack) {
|
|
112
199
|
process.stderr.write(`\n${error.stack}\n`);
|
|
113
|
-
} else {
|
|
114
|
-
process.stderr.write("
|
|
200
|
+
} else if (!isUsage) {
|
|
201
|
+
process.stderr.write(`${dim(" Re-run with --debug for the stack trace.")}\n`);
|
|
115
202
|
}
|
|
116
203
|
process.exit(1);
|
|
117
204
|
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
//#region src/dev-db/branch-pointer.ts
|
|
5
|
+
/**
|
|
6
|
+
* Which database branch this checkout is working on.
|
|
7
|
+
*
|
|
8
|
+
* `rebase db branch create` made a full copy of the database in about a second
|
|
9
|
+
* and then stopped one step short of being a feature: there was no `switch`, no
|
|
10
|
+
* `--branch` on `rebase dev`, and nothing wrote the branch anywhere a command
|
|
11
|
+
* would read it. The only way to *use* a branch was to hand-edit
|
|
12
|
+
* `DATABASE_URL`, and the documentation claimed otherwise — "the CLI updates
|
|
13
|
+
* your local development configuration" — which it did not; the `.env` was
|
|
14
|
+
* byte-identical afterwards.
|
|
15
|
+
*
|
|
16
|
+
* This is that missing step, and the shape of it is chosen to avoid three
|
|
17
|
+
* things:
|
|
18
|
+
*
|
|
19
|
+
* 1. **It does not touch `.env`.** That file holds secrets, comments and the
|
|
20
|
+
* developer's own ordering, and a command that rewrites it in place will
|
|
21
|
+
* eventually lose something that was not its business. The branch is
|
|
22
|
+
* recorded beside the other per-checkout state in `.rebase/`, which is
|
|
23
|
+
* already gitignored — a branch is a fact about one machine, never about
|
|
24
|
+
* the project.
|
|
25
|
+
*
|
|
26
|
+
* 2. **It stores a name, not a connection string.** The credentials stay in
|
|
27
|
+
* exactly one place; the URL is derived by swapping the database name on
|
|
28
|
+
* the base one at resolution time. A stored URL would be a second copy of
|
|
29
|
+
* the password, and would go stale the moment `.env` changed.
|
|
30
|
+
*
|
|
31
|
+
* 3. **It does not outrank an explicit instruction.** `--database-url` and a
|
|
32
|
+
* `DATABASE_URL` in the shell are more immediate and more specific than a
|
|
33
|
+
* switch made yesterday, so they still win. The pointer sits directly above
|
|
34
|
+
* the project's `.env`: it is a deliberate, persistent choice, and `.env` is
|
|
35
|
+
* the project default it is meant to override. Anything less and `switch`
|
|
36
|
+
* would silently do nothing on every project that sets `DATABASE_URL` —
|
|
37
|
+
* which is every project not using the managed database.
|
|
38
|
+
*/
|
|
39
|
+
var branch_pointer_exports = /* @__PURE__ */ __exportAll({
|
|
40
|
+
BRANCH_DB_PREFIX: () => "rb_",
|
|
41
|
+
branchDatabaseName: () => branchDatabaseName,
|
|
42
|
+
branchPointerPath: () => branchPointerPath,
|
|
43
|
+
branchUrl: () => branchUrl,
|
|
44
|
+
clearActiveBranch: () => clearActiveBranch,
|
|
45
|
+
databaseNameOf: () => databaseNameOf,
|
|
46
|
+
readActiveBranch: () => readActiveBranch,
|
|
47
|
+
writeActiveBranch: () => writeActiveBranch
|
|
48
|
+
});
|
|
49
|
+
/** Where the pointer lives, next to the rest of the per-checkout state. */
|
|
50
|
+
function branchPointerPath(projectRoot) {
|
|
51
|
+
return path.join(projectRoot, ".rebase", "branch.json");
|
|
52
|
+
}
|
|
53
|
+
function branchDatabaseName(name) {
|
|
54
|
+
return `rb_${name}`;
|
|
55
|
+
}
|
|
56
|
+
/** Read the active branch, or null when the checkout is on the main database. */
|
|
57
|
+
function readActiveBranch(projectRoot) {
|
|
58
|
+
try {
|
|
59
|
+
const raw = fs.readFileSync(branchPointerPath(projectRoot), "utf-8");
|
|
60
|
+
const parsed = JSON.parse(raw);
|
|
61
|
+
if (typeof parsed.name !== "string" || typeof parsed.database !== "string") return null;
|
|
62
|
+
if (!parsed.name || !parsed.database) return null;
|
|
63
|
+
return {
|
|
64
|
+
name: parsed.name,
|
|
65
|
+
database: parsed.database
|
|
66
|
+
};
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function writeActiveBranch(projectRoot, branch) {
|
|
72
|
+
const file = branchPointerPath(projectRoot);
|
|
73
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
74
|
+
fs.writeFileSync(file, `${JSON.stringify(branch, null, 2)}\n`, "utf-8");
|
|
75
|
+
}
|
|
76
|
+
/** Return to the main database. Absent is success — this is idempotent. */
|
|
77
|
+
function clearActiveBranch(projectRoot) {
|
|
78
|
+
fs.rmSync(branchPointerPath(projectRoot), { force: true });
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The base connection string with its database swapped for the branch's.
|
|
82
|
+
*
|
|
83
|
+
* Everything else is carried across untouched — credentials, host, port, and
|
|
84
|
+
* the query string, which is load-bearing here: `?sslmode=disable` is what a
|
|
85
|
+
* local Docker Postgres needs, and dropping it turns a switch into a TLS error
|
|
86
|
+
* that says nothing about branches.
|
|
87
|
+
*
|
|
88
|
+
* Returns null for a connection string that cannot be parsed, so the caller can
|
|
89
|
+
* say so plainly rather than emitting a mangled URL that fails later.
|
|
90
|
+
*/
|
|
91
|
+
function branchUrl(baseUrl, database) {
|
|
92
|
+
try {
|
|
93
|
+
const url = new URL(baseUrl);
|
|
94
|
+
url.pathname = `/${database}`;
|
|
95
|
+
return url.toString();
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** The database name in a connection string, for reporting what you are on. */
|
|
101
|
+
function databaseNameOf(connectionString) {
|
|
102
|
+
try {
|
|
103
|
+
return new URL(connectionString).pathname.replace(/^\//, "") || null;
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { branch_pointer_exports as n, readActiveBranch as r, branchUrl as t };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=branch-pointer-dBiClJ0p.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-pointer-dBiClJ0p.js","names":[],"sources":["../src/dev-db/branch-pointer.ts"],"sourcesContent":["/**\n * Which database branch this checkout is working on.\n *\n * `rebase db branch create` made a full copy of the database in about a second\n * and then stopped one step short of being a feature: there was no `switch`, no\n * `--branch` on `rebase dev`, and nothing wrote the branch anywhere a command\n * would read it. The only way to *use* a branch was to hand-edit\n * `DATABASE_URL`, and the documentation claimed otherwise — \"the CLI updates\n * your local development configuration\" — which it did not; the `.env` was\n * byte-identical afterwards.\n *\n * This is that missing step, and the shape of it is chosen to avoid three\n * things:\n *\n * 1. **It does not touch `.env`.** That file holds secrets, comments and the\n * developer's own ordering, and a command that rewrites it in place will\n * eventually lose something that was not its business. The branch is\n * recorded beside the other per-checkout state in `.rebase/`, which is\n * already gitignored — a branch is a fact about one machine, never about\n * the project.\n *\n * 2. **It stores a name, not a connection string.** The credentials stay in\n * exactly one place; the URL is derived by swapping the database name on\n * the base one at resolution time. A stored URL would be a second copy of\n * the password, and would go stale the moment `.env` changed.\n *\n * 3. **It does not outrank an explicit instruction.** `--database-url` and a\n * `DATABASE_URL` in the shell are more immediate and more specific than a\n * switch made yesterday, so they still win. The pointer sits directly above\n * the project's `.env`: it is a deliberate, persistent choice, and `.env` is\n * the project default it is meant to override. Anything less and `switch`\n * would silently do nothing on every project that sets `DATABASE_URL` —\n * which is every project not using the managed database.\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\n\n/** Where the pointer lives, next to the rest of the per-checkout state. */\nexport function branchPointerPath(projectRoot: string): string {\n return path.join(projectRoot, \".rebase\", \"branch.json\");\n}\n\nexport interface ActiveBranch {\n /** The name the developer typed. */\n name: string;\n /** The PostgreSQL database that name refers to. */\n database: string;\n}\n\n/**\n * The prefix `BranchService` puts on every branch database.\n *\n * Duplicated from the driver rather than imported because the CLI must answer\n * \"which database\" without loading a database driver — `rebase dev` reads this\n * before it knows which plugin the project uses. It is covered by a test that\n * fails if the driver's prefix moves.\n */\nexport const BRANCH_DB_PREFIX = \"rb_\";\n\nexport function branchDatabaseName(name: string): string {\n return `${BRANCH_DB_PREFIX}${name}`;\n}\n\n/** Read the active branch, or null when the checkout is on the main database. */\nexport function readActiveBranch(projectRoot: string): ActiveBranch | null {\n try {\n const raw = fs.readFileSync(branchPointerPath(projectRoot), \"utf-8\");\n const parsed = JSON.parse(raw) as Partial<ActiveBranch>;\n if (typeof parsed.name !== \"string\" || typeof parsed.database !== \"string\") return null;\n if (!parsed.name || !parsed.database) return null;\n\n return { name: parsed.name, database: parsed.database };\n } catch {\n // Absent is the common case and is not an error. A corrupt file is\n // treated the same way: the answer is \"no branch\", which is the state\n // the developer can always get back to with `branch switch --off`.\n return null;\n }\n}\n\nexport function writeActiveBranch(projectRoot: string, branch: ActiveBranch): void {\n const file = branchPointerPath(projectRoot);\n fs.mkdirSync(path.dirname(file), { recursive: true });\n fs.writeFileSync(file, `${JSON.stringify(branch, null, 2)}\\n`, \"utf-8\");\n}\n\n/** Return to the main database. Absent is success — this is idempotent. */\nexport function clearActiveBranch(projectRoot: string): void {\n fs.rmSync(branchPointerPath(projectRoot), { force: true });\n}\n\n/**\n * The base connection string with its database swapped for the branch's.\n *\n * Everything else is carried across untouched — credentials, host, port, and\n * the query string, which is load-bearing here: `?sslmode=disable` is what a\n * local Docker Postgres needs, and dropping it turns a switch into a TLS error\n * that says nothing about branches.\n *\n * Returns null for a connection string that cannot be parsed, so the caller can\n * say so plainly rather than emitting a mangled URL that fails later.\n */\nexport function branchUrl(baseUrl: string, database: string): string | null {\n try {\n const url = new URL(baseUrl);\n // `pathname` keeps its leading slash; a URL with no database at all\n // still gets one, which is what connecting to a named branch means.\n url.pathname = `/${database}`;\n\n return url.toString();\n } catch {\n return null;\n }\n}\n\n/** The database name in a connection string, for reporting what you are on. */\nexport function databaseNameOf(connectionString: string): string | null {\n try {\n const name = new URL(connectionString).pathname.replace(/^\\//, \"\");\n\n return name || null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,kBAAkB,aAA6B;CAC3D,OAAO,KAAK,KAAK,aAAa,WAAW,aAAa;AAC1D;AAmBA,SAAgB,mBAAmB,MAAsB;CACrD,OAAO,MAAsB;AACjC;;AAGA,SAAgB,iBAAiB,aAA0C;CACvE,IAAI;EACA,MAAM,MAAM,GAAG,aAAa,kBAAkB,WAAW,GAAG,OAAO;EACnE,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,IAAI,OAAO,OAAO,SAAS,YAAY,OAAO,OAAO,aAAa,UAAU,OAAO;EACnF,IAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,UAAU,OAAO;EAE7C,OAAO;GAAE,MAAM,OAAO;GAAM,UAAU,OAAO;EAAS;CAC1D,QAAQ;EAIJ,OAAO;CACX;AACJ;AAEA,SAAgB,kBAAkB,aAAqB,QAA4B;CAC/E,MAAM,OAAO,kBAAkB,WAAW;CAC1C,GAAG,UAAU,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CACpD,GAAG,cAAc,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,KAAK,OAAO;AAC1E;;AAGA,SAAgB,kBAAkB,aAA2B;CACzD,GAAG,OAAO,kBAAkB,WAAW,GAAG,EAAE,OAAO,KAAK,CAAC;AAC7D;;;;;;;;;;;;AAaA,SAAgB,UAAU,SAAiB,UAAiC;CACxE,IAAI;EACA,MAAM,MAAM,IAAI,IAAI,OAAO;EAG3B,IAAI,WAAW,IAAI;EAEnB,OAAO,IAAI,SAAS;CACxB,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgB,eAAe,kBAAyC;CACpE,IAAI;EAGA,OAFa,IAAI,IAAI,gBAAgB,CAAC,CAAC,SAAS,QAAQ,OAAO,EAExD,KAAQ;CACnB,QAAQ;EACJ,OAAO;CACX;AACJ"}
|
package/dist/bundle.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ResourceGraph, type NativeDependency, type RebaseBundleManifest, type RebaseBackendAppConfig } from "@rebasepro/types";
|
|
1
|
+
import { type ResourceGraph, type NativeDependency, type RebaseBundleManifest, type RebaseBundleFunction, type RebaseBackendAppConfig } from "@rebasepro/types";
|
|
2
2
|
export declare const DEFAULT_BUNDLE_DIR = "dist-bundle";
|
|
3
3
|
export interface BuildBundleOptions {
|
|
4
4
|
projectRoot: string;
|
|
@@ -89,15 +89,28 @@ export declare function detectNativeDependencies(projectRoot: string, declared:
|
|
|
89
89
|
* wasted space and at worst a version conflict. Workspace packages are excluded
|
|
90
90
|
* too: they are not on the registry the runtime installs from, and the project's
|
|
91
91
|
* own config package already travels inside the bundle.
|
|
92
|
+
*
|
|
93
|
+
* Throws when a declared range is not something the registry can resolve, or
|
|
94
|
+
* when two manifests declare a name at ranges nothing satisfies. The
|
|
95
|
+
* alternative is a bundle that builds green and fails at the deploy's install
|
|
96
|
+
* step, minutes later and on a machine the developer cannot see. `rebase build`
|
|
97
|
+
* reports both in its own words first; this is the backstop for every other
|
|
98
|
+
* caller, `rebase cloud deploy` included.
|
|
92
99
|
*/
|
|
93
100
|
export declare function collectDeclaredDependencies(projectRoot: string): Record<string, string>;
|
|
94
|
-
/** One
|
|
101
|
+
/** One dependency as some package.json in the project declares it. */
|
|
95
102
|
export interface DeclaredFrameworkDep {
|
|
96
103
|
name: string;
|
|
97
104
|
range: string;
|
|
98
105
|
/** Project-relative package.json it was declared in. */
|
|
99
106
|
file: string;
|
|
100
107
|
}
|
|
108
|
+
/** One name two of the bundle's manifests declare at ranges nothing satisfies. */
|
|
109
|
+
export interface DeclaredDepConflict {
|
|
110
|
+
name: string;
|
|
111
|
+
/** The two declarations, in manifest order. */
|
|
112
|
+
declarations: [DeclaredFrameworkDep, DeclaredFrameworkDep];
|
|
113
|
+
}
|
|
101
114
|
export interface FrameworkDepDrift {
|
|
102
115
|
/** Declared at a version that can never reach the CLI's own. */
|
|
103
116
|
behind: DeclaredFrameworkDep[];
|
|
@@ -107,7 +120,27 @@ export interface FrameworkDepDrift {
|
|
|
107
120
|
* packages against each other.
|
|
108
121
|
*/
|
|
109
122
|
disagreeing: string[];
|
|
123
|
+
/**
|
|
124
|
+
* Third-party names two bundle manifests declare at ranges no single version
|
|
125
|
+
* satisfies. One of the two wins in `deps.declared` and the loser's code was
|
|
126
|
+
* typechecked against a version the runtime will never install.
|
|
127
|
+
*/
|
|
128
|
+
conflicting: DeclaredDepConflict[];
|
|
110
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Names two of the bundle's manifests declare at ranges nothing satisfies.
|
|
132
|
+
*
|
|
133
|
+
* `collectDeclaredDependencies` has to pick one — the app's — and the loser is
|
|
134
|
+
* silent: the bundle tells the managed runtime to install a version the other
|
|
135
|
+
* half of the project was never compiled against. The stock scaffold shipped
|
|
136
|
+
* exactly that (`dotenv ^16` at the root, `^17.4.2` in `backend/`), so every
|
|
137
|
+
* bundle built from a default project asked for dotenv 16.
|
|
138
|
+
*
|
|
139
|
+
* Scoped to the three manifests that feed `deps.declared`, and to their
|
|
140
|
+
* `dependencies`: a `frontend/` that wants a different major of a build tool is
|
|
141
|
+
* not this problem, and `devDependencies` never reach the bundle.
|
|
142
|
+
*/
|
|
143
|
+
export declare function detectDeclaredDepConflicts(projectRoot: string): DeclaredDepConflict[];
|
|
111
144
|
/**
|
|
112
145
|
* Find `@rebasepro/*` dependencies pinned to a version older than this CLI.
|
|
113
146
|
*
|
|
@@ -126,6 +159,24 @@ export interface FrameworkDepDrift {
|
|
|
126
159
|
* one nobody looks at.
|
|
127
160
|
*/
|
|
128
161
|
export declare function detectFrameworkDepDrift(projectRoot: string, cliVersion: string): FrameworkDepDrift;
|
|
162
|
+
/**
|
|
163
|
+
* The `[start, end)` ranges of `//` and block comments in JavaScript source.
|
|
164
|
+
*
|
|
165
|
+
* `normalizeEsmSpecifiers` scans emitted output with a regular expression, and a
|
|
166
|
+
* regular expression cannot tell a specifier from a specifier somebody wrote
|
|
167
|
+
* about. The stock template ships one: `config/resources.ts` documents
|
|
168
|
+
* `import { media } from "../resources";` inside a docblock, tsc preserves the
|
|
169
|
+
* comment, and every `rebase build` of an untouched scaffold printed
|
|
170
|
+
* `⚠ 1 import(s) could not be resolved`. A warning that is wrong on the default
|
|
171
|
+
* project teaches people to ignore the warning that predicts a boot failure.
|
|
172
|
+
*
|
|
173
|
+
* Strings are tracked only so a `//` inside one — every `http://` in the tree —
|
|
174
|
+
* does not open a comment that swallows the rest of the line. A `'` or `"` span
|
|
175
|
+
* ends at the newline as well as at its closing quote: a JavaScript string
|
|
176
|
+
* cannot contain a raw newline, so a quote that reaches one was never a string
|
|
177
|
+
* (a regex literal's, most likely) and the mistake must not run on.
|
|
178
|
+
*/
|
|
179
|
+
export declare function commentSpans(source: string): Array<[number, number]>;
|
|
129
180
|
/**
|
|
130
181
|
* Rewrite relative import specifiers in emitted JavaScript so Node can resolve them.
|
|
131
182
|
*
|
|
@@ -169,6 +220,33 @@ export declare function findUnusedServerEntry(projectRoot: string, functionsDir:
|
|
|
169
220
|
/**
|
|
170
221
|
* Compile and assemble a bundle.
|
|
171
222
|
*/
|
|
223
|
+
/** What `composeBundleManifest` needs: every input, already resolved. */
|
|
224
|
+
export interface ComposeManifestInput {
|
|
225
|
+
runtimeRange: string;
|
|
226
|
+
builtAgainst: string;
|
|
227
|
+
schemaVersion: string;
|
|
228
|
+
appName: string;
|
|
229
|
+
entry: RebaseBundleManifest["entry"];
|
|
230
|
+
collectionSlugs: string[];
|
|
231
|
+
functions: RebaseBundleFunction[];
|
|
232
|
+
nativeModules: NativeDependency[];
|
|
233
|
+
declaresStorageAuthorize: boolean;
|
|
234
|
+
resources: ResourceGraph | undefined;
|
|
235
|
+
declaredDeps: Record<string, string>;
|
|
236
|
+
build: RebaseBundleManifest["build"];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The bundle manifest, as a pure function of its inputs.
|
|
240
|
+
*
|
|
241
|
+
* Separated from `buildBundle` so the SHAPE a host reads can be pinned by a
|
|
242
|
+
* test without compiling a project: `tooling/contracts/bundle-manifest.json`
|
|
243
|
+
* is written from this with fixed inputs, and the control plane's own tests
|
|
244
|
+
* read that file to prove they consume what the CLI emits. That contract is
|
|
245
|
+
* what was missing when buckets moved from `storage.sources` into `resources`
|
|
246
|
+
* and the control plane went on reading the old field for two weeks — every
|
|
247
|
+
* declared bucket arriving as nothing, with both suites green.
|
|
248
|
+
*/
|
|
249
|
+
export declare function composeBundleManifest(input: ComposeManifestInput): RebaseBundleManifest;
|
|
172
250
|
export declare function buildBundle(options: BuildBundleOptions): Promise<BuildBundleResult>;
|
|
173
251
|
/** Default install target: what the published runtime image runs. */
|
|
174
252
|
export declare const VENDOR_TARGET_OS = "linux";
|
|
@@ -339,4 +417,3 @@ export declare function buildStaticBundle(options: {
|
|
|
339
417
|
manifest: RebaseBundleManifest;
|
|
340
418
|
fileCount: number;
|
|
341
419
|
};
|
|
342
|
-
export declare function resolveCliVersion(): string;
|
package/dist/cli.d.ts
CHANGED
|
@@ -1 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flags that mean the same thing wherever they appear.
|
|
3
|
+
*
|
|
4
|
+
* `--debug` is in here even though this file does not act on it: it is
|
|
5
|
+
* implemented one level up, in `bin/rebase.js`, which prints the stack of a
|
|
6
|
+
* failed command instead of the one-line message. That is exactly why it went
|
|
7
|
+
* missing from the help for as long as it did — the flag lived in the
|
|
8
|
+
* launcher, the Options block lived here, and neither knew about the other.
|
|
9
|
+
* Declaring it here puts both under one spec, which `cli.test.ts` holds
|
|
10
|
+
* against the printed help.
|
|
11
|
+
*
|
|
12
|
+
* Exported for that test.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ROOT_FLAGS: {
|
|
15
|
+
readonly "--version": BooleanConstructor;
|
|
16
|
+
readonly "--help": BooleanConstructor;
|
|
17
|
+
readonly "--debug": BooleanConstructor;
|
|
18
|
+
readonly "-v": "--version";
|
|
19
|
+
readonly "-h": "--help";
|
|
20
|
+
};
|
|
1
21
|
export declare function entry(args: string[]): Promise<void>;
|
|
22
|
+
/** The global help. Exported so its Options block can be held against {@link ROOT_FLAGS}. */
|
|
23
|
+
export declare function printHelp(): void;
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -61,3 +61,16 @@ export declare function resolveResetPasswordArgs(rawArgs: string[]): {
|
|
|
61
61
|
email?: string;
|
|
62
62
|
password?: string;
|
|
63
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* A password for a reset that was not given one.
|
|
66
|
+
*
|
|
67
|
+
* This used to be a constant, and `--help` printed it as the default. Reset is
|
|
68
|
+
* the documented way back into an account nobody can sign in to — an admin,
|
|
69
|
+
* usually — so the recovery path set every such account to a fixed string that
|
|
70
|
+
* ships inside a public repository and a published npm package, and left it
|
|
71
|
+
* there until somebody remembered to change it.
|
|
72
|
+
*
|
|
73
|
+
* base64url of 18 random bytes: 24 characters, ~144 bits, no shell-quoting
|
|
74
|
+
* hazard, and nothing that reads like a placeholder somebody might keep.
|
|
75
|
+
*/
|
|
76
|
+
export declare function generatePassword(): string;
|
|
@@ -11,16 +11,6 @@ export interface ActionHelp {
|
|
|
11
11
|
/** Anything a caller gets wrong more than once. */
|
|
12
12
|
notes?: string[];
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Flags every cloud command accepts, documented once.
|
|
16
|
-
*
|
|
17
|
-
* Excluded from the spec comparison below — they are merged in by
|
|
18
|
-
* `parseCloudArgs` for every command in the family, so repeating them per entry
|
|
19
|
-
* would be nine copies of the same four lines.
|
|
20
|
-
*/
|
|
21
|
-
export declare const GLOBAL_HELP_FLAGS: Array<[string, string]>;
|
|
22
|
-
/** Flag names that `parseCloudArgs` adds to every command in the family. */
|
|
23
|
-
export declare const GLOBAL_SPEC_KEYS: Set<string>;
|
|
24
14
|
export declare const ACTION_HELP: Record<string, ActionHelp>;
|
|
25
15
|
/** Print one action's page — human, or its JSON description when piped. */
|
|
26
16
|
export declare function printActionHelp(entry: ActionHelp): void;
|
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every flag `rebase cloud login` accepts.
|
|
3
|
+
*
|
|
4
|
+
* Hoisted out of the `arg` call so one declaration serves the parser,
|
|
5
|
+
* `action-help.ts`'s page for this command, and the test that holds the two to
|
|
6
|
+
* each other — the same arrangement `DEPLOY_FLAGS` and `CREATE_PROJECT_FLAGS`
|
|
7
|
+
* already use.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LOGIN_FLAGS: {
|
|
10
|
+
readonly "--email": StringConstructor;
|
|
11
|
+
readonly "--password": StringConstructor;
|
|
12
|
+
readonly "-e": "--email";
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Where credentials may come from, in the order this command prefers them.
|
|
16
|
+
*
|
|
17
|
+
* The environment is the non-interactive route, and it exists so `--password`
|
|
18
|
+
* does not have to be. A password written as an argument is in the shell's
|
|
19
|
+
* history file and in the process table for as long as the command runs, and
|
|
20
|
+
* neither is something this CLI can redact after the fact — the same reasoning
|
|
21
|
+
* `rls-check` states for its connection string, which carries one too.
|
|
22
|
+
*
|
|
23
|
+
* There is no machine token yet, so CI genuinely does need a human's password;
|
|
24
|
+
* `REBASE_CLOUD_PASSWORD` is how a secret store hands it over without it
|
|
25
|
+
* appearing on a command line.
|
|
26
|
+
*/
|
|
27
|
+
export declare const PASSWORD_ENV = "REBASE_CLOUD_PASSWORD";
|
|
28
|
+
export declare const EMAIL_ENV = "REBASE_CLOUD_EMAIL";
|
|
29
|
+
/**
|
|
30
|
+
* Whether this line put a password in the shell's history.
|
|
31
|
+
*
|
|
32
|
+
* Exported for its test: the warning is the whole feature, so "it warns exactly
|
|
33
|
+
* when the flag was used" is the thing worth pinning.
|
|
34
|
+
*/
|
|
35
|
+
export declare function passwordOnTheCommandLine(args: {
|
|
36
|
+
"--password"?: string;
|
|
37
|
+
}): boolean;
|
|
1
38
|
export declare function loginCommand(rawArgs: string[]): Promise<void>;
|
|
2
39
|
export declare function logoutCommand(rawArgs: string[]): Promise<void>;
|
|
3
40
|
export declare function whoamiCommand(rawArgs: string[]): Promise<void>;
|
|
@@ -8,6 +8,19 @@ export declare function getContextOrg(url: string): string | undefined;
|
|
|
8
8
|
/** Mark a host as the active context (called on login). */
|
|
9
9
|
export declare function setCurrentContext(url: string): void;
|
|
10
10
|
export declare function resolveCloudUrl(rawArgs: string[]): string;
|
|
11
|
+
/**
|
|
12
|
+
* Refuse to run a control-plane command in a directory linked straight at a
|
|
13
|
+
* backend.
|
|
14
|
+
*
|
|
15
|
+
* The alternative — quietly using the default control plane — would be worse
|
|
16
|
+
* than the bug it replaces: the user asked for `https://api.example.com` and
|
|
17
|
+
* would get an answer about `app.rebase.pro` with nothing saying so. Naming the
|
|
18
|
+
* link, and both ways out of it, is the whole message.
|
|
19
|
+
*
|
|
20
|
+
* `--project` and `--url` are the ways out, so a line carrying either passes
|
|
21
|
+
* through: both say, explicitly, which control-plane subject the caller means.
|
|
22
|
+
*/
|
|
23
|
+
export declare function refuseDirectLink(rawArgs: string[]): void;
|
|
11
24
|
export type CloudClient = ReturnType<typeof createRebaseClient>;
|
|
12
25
|
/**
|
|
13
26
|
* Build an SDK client bound to a control-plane host, backed by the on-disk
|
|
@@ -200,7 +213,7 @@ export declare function emit<T>(human: () => void, json: JsonArg<T>): void;
|
|
|
200
213
|
* `json: unknown` accepted a function without complaint, and four call sites in
|
|
201
214
|
* `resources.ts` passed `() => ({ … })` — mirroring the human argument beside
|
|
202
215
|
* it, which *is* a thunk. In JSON mode `printJson` then stringified a function,
|
|
203
|
-
* which is `undefined`. So `rebase cloud
|
|
216
|
+
* which is `undefined`. So `rebase cloud compute`, `compute set` and two
|
|
204
217
|
* cluster commands printed the single word `undefined` on every piped or
|
|
205
218
|
* `--json` run — and since this family forces JSON mode off a TTY, that is
|
|
206
219
|
* every scripted use of them.
|
|
@@ -224,7 +237,64 @@ export type JsonArg<T> = T extends (...args: never[]) => unknown ? never : T;
|
|
|
224
237
|
* `env` shipped this shape first, alone; this generalises it so every group
|
|
225
238
|
* answers the same way.
|
|
226
239
|
*/
|
|
227
|
-
export declare function emitHelp(command: string, actions: string
|
|
240
|
+
export declare function emitHelp(command: string, actions: Array<string | HelpAction>, human: () => void, extra?: Record<string, unknown>): void;
|
|
241
|
+
/**
|
|
242
|
+
* One action on a group's help page.
|
|
243
|
+
*
|
|
244
|
+
* The page used to exist twice: a hand-formatted template literal for a
|
|
245
|
+
* terminal, and a bare list of action WORDS for everything else. So the two
|
|
246
|
+
* answers to `--help` were not the same answer — piped, `rebase cloud env
|
|
247
|
+
* --help` said `["list","set","unset","reveal","pull"]` and not one description,
|
|
248
|
+
* not one flag, and not the sentence about build-time variables that is the
|
|
249
|
+
* whole reason the page exists. This family forces JSON mode off a TTY, so that
|
|
250
|
+
* was every scripted and every agent-driven read of it.
|
|
251
|
+
*
|
|
252
|
+
* One description, rendered twice.
|
|
253
|
+
*/
|
|
254
|
+
export interface HelpAction {
|
|
255
|
+
/** The action word: `set`, `backup restore`. */
|
|
256
|
+
action: string;
|
|
257
|
+
/** The usage tail after the word: `KEY=VALUE`, `<domain>`, `[-y]`. */
|
|
258
|
+
args?: string;
|
|
259
|
+
/** One line: what it does. */
|
|
260
|
+
description: string;
|
|
261
|
+
/** Flags this action takes of its own, `[flag, description]`. */
|
|
262
|
+
flags?: Array<[string, string]>;
|
|
263
|
+
/** The heading it sits under, on a page that groups its actions. */
|
|
264
|
+
section?: string;
|
|
265
|
+
}
|
|
266
|
+
/** A whole group page — `rebase cloud env --help`. */
|
|
267
|
+
export interface GroupHelp {
|
|
268
|
+
/** The command words, no leading `rebase`: `cloud env`, or `cloud` itself. */
|
|
269
|
+
command: string;
|
|
270
|
+
/** The title's tail: "Environment variables". */
|
|
271
|
+
title: string;
|
|
272
|
+
actions: HelpAction[];
|
|
273
|
+
/** Options that belong to the group rather than to one action. */
|
|
274
|
+
options?: Array<[string, string]>;
|
|
275
|
+
/** Closing paragraphs — the things a reader gets wrong more than once. */
|
|
276
|
+
notes?: string[];
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Flags every cloud command accepts, documented once.
|
|
280
|
+
*
|
|
281
|
+
* Lives here rather than in `action-help.ts` because both help renderers print
|
|
282
|
+
* it and `action-help` imports this module, not the other way round.
|
|
283
|
+
*/
|
|
284
|
+
export declare const GLOBAL_HELP_FLAGS: Array<[string, string]>;
|
|
285
|
+
/**
|
|
286
|
+
* Flag names `parseCloudArgs` adds to every command in the family.
|
|
287
|
+
*
|
|
288
|
+
* Derived from `GLOBAL_CLOUD_FLAGS` rather than listed again — a second copy is
|
|
289
|
+
* how a global gains a spelling that the collision sweep does not know about.
|
|
290
|
+
* `--debug` is the one addition: `bin/rebase.js` takes it off `process.argv`
|
|
291
|
+
* itself, so no cloud spec declares it and every command still accepts it.
|
|
292
|
+
*/
|
|
293
|
+
export declare const GLOBAL_SPEC_KEYS: Set<string>;
|
|
294
|
+
/**
|
|
295
|
+
* Print a group's page — the human one, and the same content as JSON when piped.
|
|
296
|
+
*/
|
|
297
|
+
export declare function printGroupHelp(page: GroupHelp): void;
|
|
228
298
|
/**
|
|
229
299
|
* Print a warning (+ optional hint) — in every output mode, always to stderr.
|
|
230
300
|
*
|
|
@@ -347,6 +417,24 @@ export declare function parseCloudArgs<S extends arg.Spec>(opts: {
|
|
|
347
417
|
flags: arg.Result<S & typeof GLOBAL_CLOUD_FLAGS>;
|
|
348
418
|
positionals: string[];
|
|
349
419
|
};
|
|
420
|
+
/**
|
|
421
|
+
* Refuse an action word its group does not dispatch. Returns for `undefined`,
|
|
422
|
+
* which is every group's default action.
|
|
423
|
+
*
|
|
424
|
+
* The groups that switch on their action already do this in a `default:` case.
|
|
425
|
+
* The groups written as a chain of `if (action === "x") return …` did not: a
|
|
426
|
+
* word that matched nothing fell out of the chain into the *default* action, so
|
|
427
|
+
* `rebase cloud storage creat` listed the buckets and exited 0, and
|
|
428
|
+
* `rebase cloud billing usage` printed the account. Reporting a typo as a
|
|
429
|
+
* successful run of a different command is the failure mode this family exists
|
|
430
|
+
* to not have — an agent branching on the exit code learns nothing, and a person
|
|
431
|
+
* reads the output of a command they did not ask for.
|
|
432
|
+
*
|
|
433
|
+
* One spelling, so the code is `unknown_command` everywhere rather than the
|
|
434
|
+
* default `"error"` half of them used, and the hint always names the group's own
|
|
435
|
+
* `--help` rather than the index page.
|
|
436
|
+
*/
|
|
437
|
+
export declare function requireKnownAction(group: string, action: string | undefined, known: readonly string[]): void;
|
|
350
438
|
/**
|
|
351
439
|
* `--timeout <seconds>` as milliseconds, or `fallbackMs` when it was not given.
|
|
352
440
|
*
|