@rebasepro/cli 0.20.0 → 0.21.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/dist/bundle.d.ts +4 -0
- package/dist/commands/build.d.ts +26 -0
- package/dist/commands/cloud/bundle-deploy.d.ts +37 -0
- package/dist/commands/cloud/context.d.ts +54 -0
- package/dist/commands/cloud/databases.d.ts +2 -0
- package/dist/commands/cloud/db-connect.d.ts +70 -0
- package/dist/commands/cloud/projects.d.ts +2 -3
- package/dist/commands/init.d.ts +36 -1
- package/dist/fold-static.d.ts +3 -0
- package/dist/index.es.js +685 -122
- package/dist/index.es.js.map +1 -1
- package/dist/manifest.d.ts +20 -1
- package/dist/telemetry/consent.d.ts +1 -1
- package/package.json +10 -8
- package/templates/template/rebase.json +2 -1
package/dist/bundle.d.ts
CHANGED
|
@@ -398,6 +398,8 @@ export declare function foldStaticIntoBundle(options: {
|
|
|
398
398
|
path: string;
|
|
399
399
|
/** Serve `index.html` for unmatched paths under `path`. */
|
|
400
400
|
spa: boolean;
|
|
401
|
+
/** Where this app mounts the Rebase CMS, if it does. */
|
|
402
|
+
cms?: string;
|
|
401
403
|
}): {
|
|
402
404
|
fileCount: number;
|
|
403
405
|
dir: string;
|
|
@@ -412,6 +414,8 @@ export declare function buildStaticBundle(options: {
|
|
|
412
414
|
path?: string;
|
|
413
415
|
/** Serve `index.html` for unmatched paths. Default `true`. */
|
|
414
416
|
spa?: boolean;
|
|
417
|
+
/** Where this app mounts the Rebase CMS, if it does. */
|
|
418
|
+
cms?: string;
|
|
415
419
|
}): {
|
|
416
420
|
outDir: string;
|
|
417
421
|
manifest: RebaseBundleManifest;
|
package/dist/commands/build.d.ts
CHANGED
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import type { RebaseAppConfig } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* The two commands that turn a custom-runtime app into an image.
|
|
4
|
+
*
|
|
5
|
+
* Exported for the test, because the failure this replaced was in the printed
|
|
6
|
+
* text and nowhere else. It said:
|
|
7
|
+
*
|
|
8
|
+
* docker build -f backend/Dockerfile .
|
|
9
|
+
*
|
|
10
|
+
* for every custom backend, and the `.` was a guess — `context` was validated,
|
|
11
|
+
* stored on the config, and read by nothing. For the reference project that
|
|
12
|
+
* guess is wrong: `app/backend/Dockerfile` opens by copying `pnpm-lock.yaml`
|
|
13
|
+
* and `pnpm-workspace.yaml`, which live at the monorepo root, so the command
|
|
14
|
+
* `rebase build` handed you died on its first instruction. The deploy that
|
|
15
|
+
* actually works, `infra/cloudbuild.yaml`, has always said
|
|
16
|
+
* `-f app/backend/Dockerfile .` from the root.
|
|
17
|
+
*
|
|
18
|
+
* `dockerfile` is relative to `rebase.json`; `context` is too, and may point
|
|
19
|
+
* above it. Docker resolves `-f` against the working directory, not the
|
|
20
|
+
* context, so the path has to be re-expressed against wherever the command
|
|
21
|
+
* runs — which is what the old line never did and is the whole reason it
|
|
22
|
+
* could not be right for both.
|
|
23
|
+
*/
|
|
24
|
+
export declare function dockerBuildHint(projectRoot: string, name: string, app: {
|
|
25
|
+
dockerfile?: string;
|
|
26
|
+
context?: string;
|
|
27
|
+
}): string[];
|
|
2
28
|
export declare function buildCommand(rawArgs?: string[]): Promise<void>;
|
|
3
29
|
/**
|
|
4
30
|
* Build a static app and package it into a static bundle.
|
|
@@ -17,6 +17,35 @@ export declare function packBundle(bundleDir: string, outPath: string): Promise<
|
|
|
17
17
|
* without unpacking the uploaded archive first — a rejection (native deps, no
|
|
18
18
|
* matching runtime) is then a fast, cheap answer.
|
|
19
19
|
*/
|
|
20
|
+
/** The commit a bundle was built from, as far as the working directory knows. */
|
|
21
|
+
export interface BundleCommit {
|
|
22
|
+
hash: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The commit HEAD is on, read here because here is the only place it exists.
|
|
27
|
+
*
|
|
28
|
+
* A bundle deploy has no repository anywhere near the control plane: the CLI
|
|
29
|
+
* builds a tarball and uploads it, so the three paths `deploy.ts` documents for
|
|
30
|
+
* learning a commit — clone, `ls-remote`, or "there is no repo at all" — all
|
|
31
|
+
* resolve to the third. Every bundle deployment therefore recorded an empty
|
|
32
|
+
* hash, which is 291 of the 305 rows in production: a Deployments list where
|
|
33
|
+
* almost nothing says what it shipped.
|
|
34
|
+
*
|
|
35
|
+
* But the CLI is standing IN the repository. `git -C <dir> log -1` answers
|
|
36
|
+
* exactly, message included — the one thing even the git-build path cannot get
|
|
37
|
+
* from `ls-remote`.
|
|
38
|
+
*
|
|
39
|
+
* Returns null rather than guessing, for every reason it can fail: no git, not a
|
|
40
|
+
* repository, no commits yet. The server records what it is given and nothing
|
|
41
|
+
* more, so null here stays `UNKNOWN_COMMIT_HASH` there.
|
|
42
|
+
*
|
|
43
|
+
* A dirty tree is NOT reported as a different commit. The bundle may contain
|
|
44
|
+
* uncommitted work, and the honest statement about that is "built from a tree at
|
|
45
|
+
* <hash>", not a fabricated identifier — the same rule the rest of this file
|
|
46
|
+
* follows about inventing values.
|
|
47
|
+
*/
|
|
48
|
+
export declare function bundleCommit(cwd: string, run?: (args: string[]) => string): BundleCommit | null;
|
|
20
49
|
export declare function bundleDeployBody(input: {
|
|
21
50
|
projectId: string;
|
|
22
51
|
bundleId: string;
|
|
@@ -28,6 +57,14 @@ export declare function bundleDeployBody(input: {
|
|
|
28
57
|
* register the whole set rather than only the one being deployed.
|
|
29
58
|
*/
|
|
30
59
|
declaredApps?: DeclaredApp[];
|
|
60
|
+
/**
|
|
61
|
+
* What HEAD said when this bundle was built, or null outside a repository.
|
|
62
|
+
*
|
|
63
|
+
* Omitted from the body entirely when null — an absent field and an empty
|
|
64
|
+
* one are the same to the server, and sending `""` would make "we did not
|
|
65
|
+
* look" indistinguishable from "we looked and there was nothing".
|
|
66
|
+
*/
|
|
67
|
+
commit?: BundleCommit | null;
|
|
31
68
|
}): Record<string, unknown>;
|
|
32
69
|
/** An app as `rebase.json` declares it, reduced to what the registry stores. */
|
|
33
70
|
export interface DeclaredApp {
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import arg from "arg";
|
|
2
2
|
import { createRebaseClient } from "@rebasepro/client";
|
|
3
3
|
/** Project-local link file: <project>/.rebase/cloud.json */
|
|
4
|
+
/**
|
|
5
|
+
* The billing account an organization row points at, whichever key it arrived under.
|
|
6
|
+
*
|
|
7
|
+
* REST serves every column under its own property name, so the relation comes
|
|
8
|
+
* back as `billingAccountId`. Both readers in this CLI checked
|
|
9
|
+
* `billing_account_id` and `billingAccount` — neither is what arrives — so
|
|
10
|
+
* `rebase cloud billing` answered "no billing account" for every organization,
|
|
11
|
+
* and the deploy pre-check never saw an internal plan. The other two spellings
|
|
12
|
+
* are still accepted: older servers and fixtures send them.
|
|
13
|
+
*/
|
|
14
|
+
export declare function billingAccountIdOf(org: unknown): string | number | undefined;
|
|
4
15
|
export declare function projectLinkPath(cwd?: string): string;
|
|
5
16
|
/** Persist the active organization id for a host. */
|
|
6
17
|
export declare function setContextOrg(url: string, org: string | undefined): void;
|
|
@@ -514,3 +525,46 @@ export declare function reportError(e: unknown, context: string): never;
|
|
|
514
525
|
* rather than from a line that happens to end in a URL.
|
|
515
526
|
*/
|
|
516
527
|
export declare function openUrl(target: string, label?: string): void;
|
|
528
|
+
/**
|
|
529
|
+
* The one thing every control-plane row is required to have.
|
|
530
|
+
*
|
|
531
|
+
* The subcommands each declare their own row shape — `OrgRow`, `ProjectRow`,
|
|
532
|
+
* `DatabaseRow`, `DeploymentRow` — and every one of them is an `id` plus
|
|
533
|
+
* fields that are all optional, because a control plane older than a given
|
|
534
|
+
* column simply does not send it. The reads are written that way too
|
|
535
|
+
* (`o.name ?? "(unnamed)"`, `row.slug ? … : ""`).
|
|
536
|
+
*/
|
|
537
|
+
export interface CloudRow {
|
|
538
|
+
id: string | number;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Read the control plane's rows as a subcommand's row shape.
|
|
542
|
+
*
|
|
543
|
+
* `client.data.collection(...)` is typed against the *generated* schema of the
|
|
544
|
+
* project the SDK is pointed at, and the control-plane collections this CLI
|
|
545
|
+
* reads are not in one — so every row arrives as an open
|
|
546
|
+
* `Record<string, unknown>`. A declared `interface` gets no implicit index
|
|
547
|
+
* signature, so it does not overlap that, and a direct `as` is refused: which
|
|
548
|
+
* is how ten call sites came to write `as unknown as XRow[]`, an assertion
|
|
549
|
+
* about a wire payload with nothing checking it in either direction.
|
|
550
|
+
*
|
|
551
|
+
* There is exactly one invariant to check and this checks it. Everything else
|
|
552
|
+
* the shapes declare is optional and already read as such, so there is nothing
|
|
553
|
+
* further to verify — but a row with no usable `id` is not a row any of these
|
|
554
|
+
* commands can act on, and passing it through was how a listing came to print
|
|
555
|
+
* `[undefined]` and a lookup came to compare against the string `"undefined"`.
|
|
556
|
+
*/
|
|
557
|
+
export declare function cloudRows<T extends CloudRow>(rows: readonly Record<string, unknown>[] | undefined): T[];
|
|
558
|
+
/** {@link cloudRows} for an endpoint that returns a single row. */
|
|
559
|
+
export declare function cloudRow<T extends CloudRow>(row: Record<string, unknown> | undefined): T | undefined;
|
|
560
|
+
/**
|
|
561
|
+
* {@link cloudRows} for a write that must have produced a row.
|
|
562
|
+
*
|
|
563
|
+
* `create()` returning something with no usable `id` means the control plane
|
|
564
|
+
* accepted the write and then described it in a way this CLI cannot act on.
|
|
565
|
+
* The callers all go straight on to use that id — `setContextOrg(url,
|
|
566
|
+
* String(created.id))` — so asserting the shape, which is what stood here,
|
|
567
|
+
* turned a control-plane fault into an organization whose active id is the
|
|
568
|
+
* seven-letter string `"undefined"`, stored in the user's config file.
|
|
569
|
+
*/
|
|
570
|
+
export declare function requireCloudRow<T extends CloudRow>(row: Record<string, unknown> | undefined, what: string): T;
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* db list List databases attached to the project
|
|
5
5
|
* db create Attach a managed or bring-your-own database
|
|
6
|
+
* db info Where the database is, and what it is called
|
|
7
|
+
* db connect A local port that IS the project's database
|
|
6
8
|
* db test Test connectivity to the project's database
|
|
7
9
|
* db backup list|create|restore
|
|
8
10
|
*/
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `rebase cloud db connect` — a local port that is your cloud database.
|
|
3
|
+
*
|
|
4
|
+
* ## What this replaces
|
|
5
|
+
*
|
|
6
|
+
* A managed database lives in a namespace of the platform's cluster, and its
|
|
7
|
+
* address (`postgres-rw.rebase-tenant-….svc.cluster.local`) resolves to nothing
|
|
8
|
+
* on a developer's machine. The console used to bridge that gap by printing
|
|
9
|
+
*
|
|
10
|
+
* kubectl port-forward svc/postgres-rw -n rebase-tenant-… 5432:5432
|
|
11
|
+
*
|
|
12
|
+
* which nobody outside the platform can run: a tenant of Rebase Cloud has no
|
|
13
|
+
* kubeconfig for our cluster, and there is no product that sells one. So the
|
|
14
|
+
* platform reaches into the cluster instead, and this command is the local end
|
|
15
|
+
* of that reach.
|
|
16
|
+
*
|
|
17
|
+
* ## How it works
|
|
18
|
+
*
|
|
19
|
+
* A listener on 127.0.0.1. Every TCP connection it accepts opens its own
|
|
20
|
+
* WebSocket to the control plane, authenticates in-band with the console session
|
|
21
|
+
* this CLI already holds, and from `ready` onwards the two are a byte pipe. The
|
|
22
|
+
* database still asks for a password — the tunnel is a network path, not a
|
|
23
|
+
* credential — so `psql` behaves exactly as it would against a local Postgres.
|
|
24
|
+
*
|
|
25
|
+
* One WebSocket per connection rather than one multiplexed socket: `psql` is one
|
|
26
|
+
* connection and a pool is a handful, and per-connection sockets keep the
|
|
27
|
+
* framing at "these bytes are those bytes" with no stream ids to get wrong.
|
|
28
|
+
*
|
|
29
|
+
* Node's global `WebSocket` is used rather than `ws`, which is why the token
|
|
30
|
+
* goes in the first frame instead of an `Authorization` header — the WHATWG
|
|
31
|
+
* client cannot set request headers, and a header-only endpoint would be
|
|
32
|
+
* unreachable from a browser too.
|
|
33
|
+
*/
|
|
34
|
+
import net from "node:net";
|
|
35
|
+
/** Documented in `action-help.ts`, and paired with it by `action-help.test.ts`. */
|
|
36
|
+
export declare const DB_CONNECT_FLAGS: {
|
|
37
|
+
readonly "--port": NumberConstructor;
|
|
38
|
+
readonly "--reveal": BooleanConstructor;
|
|
39
|
+
};
|
|
40
|
+
/** `https://app.rebase.pro` → `wss://app.rebase.pro/api/db-tunnel/p1`. */
|
|
41
|
+
export declare function tunnelUrl(cloudUrl: string, projectId: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* A local DSN for the tunnel, with the password only if the caller asked.
|
|
44
|
+
*
|
|
45
|
+
* Built here and nowhere else. The server reports the *cluster's* URI, which is
|
|
46
|
+
* the one thing that must not be printed as the way to connect — it is exactly
|
|
47
|
+
* the address that does not work from here.
|
|
48
|
+
*/
|
|
49
|
+
export declare function localDsn(opts: {
|
|
50
|
+
port: number;
|
|
51
|
+
username: string | null;
|
|
52
|
+
database: string | null;
|
|
53
|
+
password?: string;
|
|
54
|
+
}): string;
|
|
55
|
+
export declare function dbConnect(rawArgs: string[]): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* One accepted connection, carried over one WebSocket.
|
|
58
|
+
*
|
|
59
|
+
* The local socket is paused until the tunnel says `ready`, so a client that
|
|
60
|
+
* sends its startup packet the instant it connects — which every Postgres
|
|
61
|
+
* client does — cannot have those bytes arrive before there is a database to
|
|
62
|
+
* send them to.
|
|
63
|
+
*
|
|
64
|
+
* Exported for `db-connect.pipe.test.ts`, which drives it with a real socket
|
|
65
|
+
* against a real WebSocket: this is the half a developer's client actually
|
|
66
|
+
* talks to, and a pipe is the one component whose bugs are silent — a dropped
|
|
67
|
+
* or reordered chunk does not throw, it corrupts a Postgres message and
|
|
68
|
+
* surfaces as a protocol error nowhere near here.
|
|
69
|
+
*/
|
|
70
|
+
export declare function pipeThroughTunnel(socket: net.Socket, endpoint: string, token: string): void;
|
|
@@ -53,8 +53,8 @@ export declare const CREATE_PROJECT_FLAGS: {
|
|
|
53
53
|
* means the two-command sequence that every project needs is one command,
|
|
54
54
|
* and `--db none` is there for the case that genuinely wants to decide later.
|
|
55
55
|
*
|
|
56
|
-
* Distinct from `--db-
|
|
57
|
-
* on a database that exists. This is whether there is one.
|
|
56
|
+
* Distinct from `--db-cpu`/`--db-instances` next to it, which are resource
|
|
57
|
+
* dials on a database that exists. This is whether there is one.
|
|
58
58
|
*/
|
|
59
59
|
readonly "--db": StringConstructor;
|
|
60
60
|
/** For `--db byodb`. Same spelling as `rebase cloud db create` uses. */
|
|
@@ -65,7 +65,6 @@ export declare const CREATE_PROJECT_FLAGS: {
|
|
|
65
65
|
readonly "--replicas": StringConstructor;
|
|
66
66
|
readonly "--spot": StringConstructor;
|
|
67
67
|
readonly "--scale-to-zero": StringConstructor;
|
|
68
|
-
readonly "--db-mode": StringConstructor;
|
|
69
68
|
readonly "--db-instances": StringConstructor;
|
|
70
69
|
readonly "--db-cpu": StringConstructor;
|
|
71
70
|
readonly "--db-memory": StringConstructor;
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -56,7 +56,42 @@ export interface BuildQuestionsParams {
|
|
|
56
56
|
* Exported for testability — all prompt `type` values must match
|
|
57
57
|
* types registered by the installed version of inquirer.
|
|
58
58
|
*/
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The questions this command asks, as inquirer types them.
|
|
61
|
+
*
|
|
62
|
+
* Declared, rather than left as `Record<string, unknown>` and asserted into
|
|
63
|
+
* `Parameters<typeof inquirer.prompt>[0]` at the call. Inquirer infers the
|
|
64
|
+
* answer shape from the array it is handed, so an array of open records matches
|
|
65
|
+
* none of its overloads — which is the refusal that assertion was there to get
|
|
66
|
+
* past, at the cost of every question in this file being unchecked: a
|
|
67
|
+
* misspelled `mesage`, a `choices` on a `confirm`, a `default` of the wrong
|
|
68
|
+
* type for its question kind all compiled.
|
|
69
|
+
*/
|
|
70
|
+
export type InitQuestion = {
|
|
71
|
+
type: "input";
|
|
72
|
+
name: string;
|
|
73
|
+
message: string;
|
|
74
|
+
default: string;
|
|
75
|
+
validate?: (input: string) => string | true;
|
|
76
|
+
} | {
|
|
77
|
+
type: "confirm";
|
|
78
|
+
name: string;
|
|
79
|
+
message: string;
|
|
80
|
+
default: boolean;
|
|
81
|
+
when?: (answers: Record<string, unknown>) => boolean;
|
|
82
|
+
} | {
|
|
83
|
+
type: "select";
|
|
84
|
+
name: string;
|
|
85
|
+
message: string;
|
|
86
|
+
choices: ReadonlyArray<{
|
|
87
|
+
name: string;
|
|
88
|
+
value: unknown;
|
|
89
|
+
short: string;
|
|
90
|
+
}>;
|
|
91
|
+
default: unknown;
|
|
92
|
+
when?: (answers: Record<string, unknown>) => boolean;
|
|
93
|
+
};
|
|
94
|
+
export declare function buildInitQuestions(params: BuildQuestionsParams): InitQuestion[];
|
|
60
95
|
/**
|
|
61
96
|
* The `cd` a user must type to enter the new project.
|
|
62
97
|
*
|
package/dist/fold-static.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface FoldableManifest {
|
|
|
6
6
|
output?: string;
|
|
7
7
|
path?: string;
|
|
8
8
|
spa?: boolean;
|
|
9
|
+
cms?: string;
|
|
9
10
|
}>;
|
|
10
11
|
}
|
|
11
12
|
export interface FoldOptions {
|
|
@@ -32,6 +33,8 @@ export interface FoldableApp {
|
|
|
32
33
|
path: string;
|
|
33
34
|
/** SPA fallback, defaulted to `true`. */
|
|
34
35
|
spa: boolean;
|
|
36
|
+
/** Where this app mounts the Rebase CMS, if it does. */
|
|
37
|
+
cms?: string;
|
|
35
38
|
}
|
|
36
39
|
/**
|
|
37
40
|
* Every static app in the manifest, in mount order.
|