@rebasepro/cli 0.12.1-canary.gf5f1d39 → 0.13.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/bin/rebase.js +27 -1
- package/dist/bundle.d.ts +36 -0
- package/dist/commands/cloud/context.d.ts +78 -0
- package/dist/commands/cloud/deploy.d.ts +64 -0
- package/dist/commands/cloud/index.d.ts +23 -0
- package/dist/commands/cloud/projects.d.ts +32 -1
- package/dist/commands/dev.d.ts +16 -0
- package/dist/commands/telemetry.d.ts +9 -0
- package/dist/index.es.js +974 -69
- package/dist/index.es.js.map +1 -1
- package/dist/telemetry/consent.d.ts +38 -0
- package/dist/telemetry/identity.d.ts +69 -0
- package/dist/telemetry/index.d.ts +72 -0
- package/dist/telemetry/payload.d.ts +78 -0
- package/dist/telemetry/project.d.ts +34 -0
- package/package.json +11 -11
- package/templates/overlays/baas/backend/package.json +2 -2
- package/templates/overlays/baas/package.json +1 -2
- package/templates/template/backend/package.json +2 -2
- package/templates/template/config/collections/index.ts +9 -1
- package/templates/template/frontend/package.json +3 -3
- package/templates/template/frontend/src/App.tsx +2 -1
- package/templates/template/frontend/src/main.tsx +2 -1
- package/templates/template/frontend/vite.config.ts +0 -1
- package/templates/template/package.json +1 -2
package/bin/rebase.js
CHANGED
|
@@ -88,4 +88,30 @@ if (!existsSync(distEntry)) {
|
|
|
88
88
|
|
|
89
89
|
const { entry } = await import("../dist/index.es.js");
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
/**
|
|
92
|
+
* The CLI's last line of defence.
|
|
93
|
+
*
|
|
94
|
+
* `entry()` returns a promise and nothing was awaiting it, so anything a
|
|
95
|
+
* command threw surfaced as an unhandled rejection: Node's own stack trace,
|
|
96
|
+
* rooted in `dist/index.es.js`, with the CLI's bundled line numbers and no
|
|
97
|
+
* exit code of its own. "Collections directory not found" is a sentence a
|
|
98
|
+
* developer can act on; the same sentence under ten frames of bundle internals
|
|
99
|
+
* reads as a crash in Rebase.
|
|
100
|
+
*
|
|
101
|
+
* The message is the error's own — commands that already print something
|
|
102
|
+
* friendly and exit never reach here. The stack is available behind
|
|
103
|
+
* `--debug`/`REBASE_DEBUG`, because when the message is *not* enough that is
|
|
104
|
+
* the only thing that helps.
|
|
105
|
+
*/
|
|
106
|
+
const wantsStack = process.argv.includes("--debug") || process.env.REBASE_DEBUG === "1";
|
|
107
|
+
|
|
108
|
+
entry(process.argv).catch((error) => {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
process.stderr.write(`\x1b[31m✗ ${message}\x1b[0m\n`);
|
|
111
|
+
if (wantsStack && error instanceof Error && error.stack) {
|
|
112
|
+
process.stderr.write(`\n${error.stack}\n`);
|
|
113
|
+
} else {
|
|
114
|
+
process.stderr.write("\x1b[90m Re-run with --debug for the stack trace.\x1b[0m\n");
|
|
115
|
+
}
|
|
116
|
+
process.exit(1);
|
|
117
|
+
});
|
package/dist/bundle.d.ts
CHANGED
|
@@ -68,6 +68,41 @@ export declare function detectNativeDependencies(projectRoot: string, declared:
|
|
|
68
68
|
* own config package already travels inside the bundle.
|
|
69
69
|
*/
|
|
70
70
|
export declare function collectDeclaredDependencies(projectRoot: string): Record<string, string>;
|
|
71
|
+
/** One `@rebasepro/*` dependency as some package.json in the project declares it. */
|
|
72
|
+
export interface DeclaredFrameworkDep {
|
|
73
|
+
name: string;
|
|
74
|
+
range: string;
|
|
75
|
+
/** Project-relative package.json it was declared in. */
|
|
76
|
+
file: string;
|
|
77
|
+
}
|
|
78
|
+
export interface FrameworkDepDrift {
|
|
79
|
+
/** Declared at a version that can never reach the CLI's own. */
|
|
80
|
+
behind: DeclaredFrameworkDep[];
|
|
81
|
+
/**
|
|
82
|
+
* The distinct lower bounds found across all declared `@rebasepro/*`, when
|
|
83
|
+
* there is more than one — the project is pinning mixed-era framework
|
|
84
|
+
* packages against each other.
|
|
85
|
+
*/
|
|
86
|
+
disagreeing: string[];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Find `@rebasepro/*` dependencies pinned to a version older than this CLI.
|
|
90
|
+
*
|
|
91
|
+
* This is the only place a developer can be told. In development, every
|
|
92
|
+
* `@rebasepro/*` resolves through pnpm's `link:`/`workspace:` overrides to the
|
|
93
|
+
* checkout, so the version STRINGS in package.json are never exercised — the
|
|
94
|
+
* project runs fine locally on whatever is on disk, and the declared numbers are
|
|
95
|
+
* first honoured when the runtime npm-installs them from a bundle in the cloud.
|
|
96
|
+
* A project scaffolded at 0.10.0 therefore keeps working on a developer's
|
|
97
|
+
* machine indefinitely while being, in the cloud, a 0.10.0 driver.
|
|
98
|
+
*
|
|
99
|
+
* That matters because the image supplies only `@rebasepro/server`; the database
|
|
100
|
+
* driver comes from these declarations and a newer runtime never updates it.
|
|
101
|
+
* Every package.json is scanned, `dependencies` and `devDependencies` both,
|
|
102
|
+
* because they have to be bumped together and the one that gets forgotten is the
|
|
103
|
+
* one nobody looks at.
|
|
104
|
+
*/
|
|
105
|
+
export declare function detectFrameworkDepDrift(projectRoot: string, cliVersion: string): FrameworkDepDrift;
|
|
71
106
|
/**
|
|
72
107
|
* Rewrite relative import specifiers in emitted JavaScript so Node can resolve them.
|
|
73
108
|
*
|
|
@@ -182,3 +217,4 @@ export declare function buildStaticBundle(options: {
|
|
|
182
217
|
manifest: RebaseBundleManifest;
|
|
183
218
|
fileCount: number;
|
|
184
219
|
};
|
|
220
|
+
export declare function resolveCliVersion(): string;
|
|
@@ -24,7 +24,32 @@ export declare function requireClient(rawArgs: string[]): Promise<{
|
|
|
24
24
|
client: CloudClient;
|
|
25
25
|
url: string;
|
|
26
26
|
}>;
|
|
27
|
+
/** One place a deploy can actually land, as the control plane describes it. */
|
|
28
|
+
export interface DeployTarget {
|
|
29
|
+
clusterId?: string | null;
|
|
30
|
+
provider: string;
|
|
31
|
+
region?: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
baseDomain?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface PlatformConfig {
|
|
36
|
+
tenantBaseDomain?: string;
|
|
37
|
+
deployTargets?: DeployTarget[];
|
|
38
|
+
}
|
|
39
|
+
export declare function fetchPlatformConfig(client: CloudClient, url: string): Promise<PlatformConfig | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* The base domain tenant projects are served at, derived from the same
|
|
42
|
+
* TENANT_BASE_DOMAIN the ingress and the console read (see
|
|
43
|
+
* saas/backend/src/utils/tenant-domain.ts).
|
|
44
|
+
*/
|
|
27
45
|
export declare function fetchTenantBaseDomain(client: CloudClient, url: string): Promise<string | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* The infrastructure a deploy for this control plane would ACTUALLY use, in the
|
|
48
|
+
* resolver's own preference order (saas/backend/src/k8s/resolve.ts).
|
|
49
|
+
*
|
|
50
|
+
* @returns the targets, or `undefined` when the control plane cannot say.
|
|
51
|
+
*/
|
|
52
|
+
export declare function fetchDeployTargets(client: CloudClient, url: string): Promise<DeployTarget[] | undefined>;
|
|
28
53
|
/**
|
|
29
54
|
* Public host for a project — `<subdomain>.<base>`, or the bare subdomain when
|
|
30
55
|
* the base domain is unknown.
|
|
@@ -84,6 +109,39 @@ export interface ProjectLink {
|
|
|
84
109
|
export declare function readLink(cwd?: string): ProjectLink | null;
|
|
85
110
|
export declare function writeLink(link: ProjectLink, cwd?: string): void;
|
|
86
111
|
export declare function removeLink(cwd?: string): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Flags that may appear anywhere on a `rebase cloud` line, including *before*
|
|
114
|
+
* the resource group.
|
|
115
|
+
*
|
|
116
|
+
* They have to be declared wherever positionals are resolved, because `arg`'s
|
|
117
|
+
* `permissive: true` does not merely tolerate an undeclared flag — it pushes it
|
|
118
|
+
* into `_` alongside the positionals, and for a flag that takes a value it
|
|
119
|
+
* pushes the value in too. So `cloud --project acme storage create` parsed
|
|
120
|
+
* without this spec yields `_` of `["--project", "acme", "storage", "create"]`,
|
|
121
|
+
* and the group reads as `"acme"`: a real project name, in the group position,
|
|
122
|
+
* dispatching to nothing. Skipping tokens that start with `-` does not save you
|
|
123
|
+
* there — the damage is the orphaned value, which looks exactly like a
|
|
124
|
+
* positional.
|
|
125
|
+
*
|
|
126
|
+
* Only genuinely global flags belong here. Group-specific ones (`--bucket`,
|
|
127
|
+
* `--region`, …) are declared by the handler that owns them and always follow
|
|
128
|
+
* the group, so they cannot shift the group or action.
|
|
129
|
+
*
|
|
130
|
+
* `-p` is `--project` in eighteen places and `--password` in `login`. That
|
|
131
|
+
* ambiguity does not matter to the one caller that reads this spec: it resolves
|
|
132
|
+
* positionals and never looks at a flag's value, so all it needs to know is
|
|
133
|
+
* that `-p` takes one. Anything that wants the value must keep declaring it
|
|
134
|
+
* itself, with the meaning its own command gives it.
|
|
135
|
+
*/
|
|
136
|
+
export declare const GLOBAL_CLOUD_FLAGS: {
|
|
137
|
+
readonly "--json": BooleanConstructor;
|
|
138
|
+
readonly "--yes": BooleanConstructor;
|
|
139
|
+
readonly "--help": BooleanConstructor;
|
|
140
|
+
readonly "--project": StringConstructor;
|
|
141
|
+
readonly "-p": "--project";
|
|
142
|
+
readonly "-y": "--yes";
|
|
143
|
+
readonly "-h": "--help";
|
|
144
|
+
};
|
|
87
145
|
/**
|
|
88
146
|
* The raw project reference to operate on: explicit `--project` flag wins,
|
|
89
147
|
* otherwise the linked project. Exits with guidance when neither is present.
|
|
@@ -134,6 +192,26 @@ export declare function printJson(value: unknown): void;
|
|
|
134
192
|
* call is what guarantees a command can never print a table AND a JSON blob.
|
|
135
193
|
*/
|
|
136
194
|
export declare function emit(human: () => void, json: unknown): void;
|
|
195
|
+
/**
|
|
196
|
+
* Print a warning (+ optional hint) — in every output mode, always to stderr.
|
|
197
|
+
*
|
|
198
|
+
* `emit` is for a command's *result*, and JSON mode legitimately replaces the
|
|
199
|
+
* human rendering of one. A warning is not a result: it says the command is
|
|
200
|
+
* about to do something the caller may not have meant, and that is exactly as
|
|
201
|
+
* true when the output is piped. Gating one on `!isJsonMode()` deleted it
|
|
202
|
+
* precisely where nobody was watching the terminal — a `--source` deploy ejected
|
|
203
|
+
* a live project off the managed runtime and said so only to a TTY that wasn't
|
|
204
|
+
* there.
|
|
205
|
+
*
|
|
206
|
+
* stdout carries the JSON value and nothing else, so warnings go to stderr:
|
|
207
|
+
* a machine parser reading stdout cannot be corrupted by one. Only the
|
|
208
|
+
* *formatting* may depend on the mode — colour and indentation for a terminal,
|
|
209
|
+
* plain ASCII otherwise. Whether a warning is emitted at all may not.
|
|
210
|
+
*
|
|
211
|
+
* Anything a caller might branch on belongs in the JSON payload as well; stderr
|
|
212
|
+
* is for whoever reads the transcript afterwards.
|
|
213
|
+
*/
|
|
214
|
+
export declare function warn(message: string, hint?: string): void;
|
|
137
215
|
/** Print an error (+ optional hint) and exit non-zero. Never returns. */
|
|
138
216
|
export declare function fail(message: string, hint?: string, code?: string): never;
|
|
139
217
|
/**
|
|
@@ -46,5 +46,69 @@ export declare function timeAgo(value: string | Date | undefined, now: Date): st
|
|
|
46
46
|
export declare function isManagedProject(project: DeployProjectRow | undefined, latest: DeploySourceRow | undefined): boolean;
|
|
47
47
|
/** What a `deploy` with nothing attached will build, in the words to print. */
|
|
48
48
|
export declare function planBareDeploy(project: DeployProjectRow | undefined, latest: DeploySourceRow | undefined, now: Date): BareDeployPlan;
|
|
49
|
+
/**
|
|
50
|
+
* A warning attached to a deploy: printed for the human, carried in the JSON.
|
|
51
|
+
*
|
|
52
|
+
* `code` is the stable half — the message is prose and will be reworded, so it
|
|
53
|
+
* is the code that CI or an agent branches on.
|
|
54
|
+
*/
|
|
55
|
+
export interface DeployWarning {
|
|
56
|
+
code: string;
|
|
57
|
+
message: string;
|
|
58
|
+
hint?: string;
|
|
59
|
+
}
|
|
60
|
+
/** `code` of the warning below, and the field name it sets in the payload. */
|
|
61
|
+
export declare const EJECTS_MANAGED_RUNTIME = "ejects_managed_runtime";
|
|
62
|
+
/** The one sentence that says a source build undoes `runtimeMode: managed`. */
|
|
63
|
+
export declare function ejectWarning(projectRef: string): DeployWarning;
|
|
64
|
+
/** How a container-image deploy was asked for — the input to both rules below. */
|
|
65
|
+
export interface EjectContext {
|
|
66
|
+
/** The project currently runs on the managed runtime. */
|
|
67
|
+
managed: boolean;
|
|
68
|
+
/** `--source` was passed: build this directory. */
|
|
69
|
+
source: boolean;
|
|
70
|
+
/** `--force` was passed: eject on purpose. */
|
|
71
|
+
force: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Why a container-image deploy of a managed project is refused — or `undefined`
|
|
75
|
+
* to let it through.
|
|
76
|
+
*
|
|
77
|
+
* Every path below this point builds a container image, and a successful one
|
|
78
|
+
* sets `runtimeMode: "custom"` server-side. So the question is never "which flag
|
|
79
|
+
* was used" but "did the caller ask to leave the managed runtime", and only
|
|
80
|
+
* `--force` answers it.
|
|
81
|
+
*
|
|
82
|
+
* `--source` used to be read as answering it too, on the theory that uploading a
|
|
83
|
+
* build context is self-evidently a deliberate eject. It is not: `--source`
|
|
84
|
+
* picks *which source* gets built — this directory, rather than the stale
|
|
85
|
+
* archive the control plane is holding — and the eject is a side effect of the
|
|
86
|
+
* answer. That is exactly how a live project got flipped to `custom` by someone
|
|
87
|
+
* whose actual intent was "deploy what I have here", and it is the same
|
|
88
|
+
* ignorance the bare form is refused for. Same ignorance, same refusal.
|
|
89
|
+
*/
|
|
90
|
+
export declare function ejectRefusal(opts: EjectContext, projectRef: string): {
|
|
91
|
+
message: string;
|
|
92
|
+
hint: string;
|
|
93
|
+
code: string;
|
|
94
|
+
} | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Which warnings a container-image deploy has earned.
|
|
97
|
+
*
|
|
98
|
+
* Pure, and separate from the printing, because the printing is what went
|
|
99
|
+
* wrong: the eject warning used to be written inline behind `!isJsonMode()`, so
|
|
100
|
+
* the fact that a deploy ejects a managed project existed only as a side effect
|
|
101
|
+
* of a TTY being attached. Deciding here, emitting once at the call site, means
|
|
102
|
+
* the decision cannot be output-mode-dependent again.
|
|
103
|
+
*
|
|
104
|
+
* The condition is just `managed`: anything reaching this point is a container
|
|
105
|
+
* image build that `ejectRefusal` has already let through, and on a managed
|
|
106
|
+
* project that is an eject however it was spelled. A caller who passed `--force`
|
|
107
|
+
* knows — the warning is for the transcript and the payload, which is what
|
|
108
|
+
* anyone reviewing the deploy afterwards actually reads.
|
|
109
|
+
*/
|
|
110
|
+
export declare function deployWarnings(opts: EjectContext, projectRef: string): DeployWarning[];
|
|
111
|
+
/** The warning half of a deploy's JSON payload — merged into whatever it emits. */
|
|
112
|
+
export declare function warningPayload(warnings: DeployWarning[]): Record<string, unknown>;
|
|
49
113
|
export declare function deployCommand(rawArgs: string[], projectRef: string): Promise<void>;
|
|
50
114
|
export declare function logsCommand(rawArgs: string[], projectRef: string): Promise<void>;
|
|
@@ -1 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positional tokens after `rebase cloud` (group, action, …).
|
|
3
|
+
*
|
|
4
|
+
* Two things stop a flag being mistaken for the group. `GLOBAL_CLOUD_FLAGS` is
|
|
5
|
+
* declared so `arg` *consumes* the flags that may precede it — critically
|
|
6
|
+
* together with their values, which is the half that filtering cannot do. The
|
|
7
|
+
* leading-`-` skip then covers a flag nobody declared, so an unrecognised
|
|
8
|
+
* boolean shifts nothing.
|
|
9
|
+
*
|
|
10
|
+
* Only leading tokens are skipped: past the group and action, an undeclared
|
|
11
|
+
* flag and its value are somebody else's positionals and none of our business.
|
|
12
|
+
* A flag this file has never heard of, that takes a value, placed before the
|
|
13
|
+
* group, is the one shape still unresolvable here — there is no way to know
|
|
14
|
+
* whether the token after it is its value or the group, and guessing either way
|
|
15
|
+
* is worse than the handler reporting an unknown group.
|
|
16
|
+
*
|
|
17
|
+
* Exported so its tests can drive the real thing. The dispatch test used to
|
|
18
|
+
* re-implement it locally as `slice(3).filter(a => !a.startsWith("-"))` — which
|
|
19
|
+
* filtered flags, while this function did not — so the test asserted the
|
|
20
|
+
* behaviour we wanted against a copy that had it, and stayed green for as long
|
|
21
|
+
* as the real dispatcher was broken.
|
|
22
|
+
*/
|
|
23
|
+
export declare function positionals(rawArgs: string[]): string[];
|
|
1
24
|
export declare function cloudCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
|
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
import { type CloudClient } from "./context";
|
|
1
|
+
import { type DeployTarget, type CloudClient } from "./context";
|
|
2
2
|
export declare function listProjects(rawArgs: string[]): Promise<void>;
|
|
3
|
+
/**
|
|
4
|
+
* Where this project says it runs.
|
|
5
|
+
*
|
|
6
|
+
* `provider`/`region` are a *request*: no code downstream reads them to pick a
|
|
7
|
+
* deploy target — that comes from the project's cluster record or the ambient
|
|
8
|
+
* in-cluster context (saas/backend/src/k8s/resolve.ts). So a wrong value here is
|
|
9
|
+
* never contradicted by a failure; it just sits in the record. The CLI used to
|
|
10
|
+
* default to `hetzner`/`nbg1` unconditionally, which is how projects running on
|
|
11
|
+
* our GKE cluster came to describe themselves as Hetzner in the console — and
|
|
12
|
+
* `provider` is half the Stripe compute lookup key (`compute_<provider>_<vmSize>`),
|
|
13
|
+
* so that is a mispricing, not a cosmetic slip.
|
|
14
|
+
*
|
|
15
|
+
* The control plane already publishes the infrastructure that actually exists,
|
|
16
|
+
* and the console's create wizard reads it. Ask the same question here.
|
|
17
|
+
*
|
|
18
|
+
* Exported for tests: the decision is pure, so it can be pinned without a
|
|
19
|
+
* control plane. The fetching and the exit live in `resolveRequestedTarget`.
|
|
20
|
+
*
|
|
21
|
+
* @param requested `--provider`, if the caller named one. An explicit flag wins:
|
|
22
|
+
* it is the caller stating intent, and `deploy` corrects the record anyway.
|
|
23
|
+
* @param targets What the control plane says exists, or `undefined` when it
|
|
24
|
+
* cannot say — an older deployment with no `platform-config`, or a failed
|
|
25
|
+
* request. That is different from an empty list, which is a control plane
|
|
26
|
+
* stating it has no infrastructure at all.
|
|
27
|
+
* @returns the target to record, or `null` when the control plane answered that
|
|
28
|
+
* there is none.
|
|
29
|
+
*/
|
|
30
|
+
export declare function chooseRequestedTarget(requested: string | undefined, targets: DeployTarget[] | undefined): {
|
|
31
|
+
provider: string;
|
|
32
|
+
region?: string;
|
|
33
|
+
} | null;
|
|
3
34
|
export declare function createProject(rawArgs: string[]): Promise<void>;
|
|
4
35
|
export declare function projectInfo(rawArgs: string[], projectRef: string): Promise<void>;
|
|
5
36
|
export declare function deleteProject(rawArgs: string[], projectRef: string): Promise<void>;
|
package/dist/commands/dev.d.ts
CHANGED
|
@@ -1 +1,17 @@
|
|
|
1
|
+
/** Well-known filename the backend writes its actual port to. */
|
|
2
|
+
export declare const DEV_PORT_FILENAME = ".rebase-dev-port";
|
|
3
|
+
/**
|
|
4
|
+
* Compute a deterministic port from the project root path.
|
|
5
|
+
* Range: 3001–3999 (avoids privileged ports and common services).
|
|
6
|
+
* Two different project directories will almost always get different ports.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getProjectPort(projectRoot: string): number;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the best starting port for this project:
|
|
11
|
+
* 1. Explicit --port flag (highest priority)
|
|
12
|
+
* 2. PORT env var
|
|
13
|
+
* 3. Previously used port from .rebase-dev-port (port affinity across restarts)
|
|
14
|
+
* 4. Deterministic hash from project path (unique per project)
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveStartPort(projectRoot: string, explicitPort?: number): number;
|
|
1
17
|
export declare function devCommand(rawArgs: string[]): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `rebase telemetry` — the command that makes the rest of it inspectable.
|
|
3
|
+
*
|
|
4
|
+
* The whole subsystem asks for trust it cannot otherwise earn, and the cheapest
|
|
5
|
+
* way to earn it is to stop describing the payload and print it. `show` runs
|
|
6
|
+
* the same builder the sender uses, so what appears here is what would go, not
|
|
7
|
+
* a documentation comment that quietly fell out of date two releases ago.
|
|
8
|
+
*/
|
|
9
|
+
export declare function telemetryCommand(rawArgs: string[]): Promise<void>;
|