@rebasepro/cli 0.21.0 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -3
- package/bin/rebase.js +4 -1
- package/dist/commands/cloud/bundle-deploy.d.ts +9 -0
- package/dist/commands/cloud/deploy.d.ts +33 -0
- package/dist/commands/cloud/errors.d.ts +5 -0
- package/dist/commands/cloud/rebuild-source.d.ts +110 -0
- package/dist/commands/cloud/settings.d.ts +6 -1
- package/dist/commands/upgrade.d.ts +38 -0
- package/dist/index.es.js +1605 -62
- package/dist/index.es.js.map +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/upgrade.d.ts +145 -0
- package/package.json +7 -7
- package/templates/overlays/baas/README.md +5 -3
- package/templates/overlays/baas/ai-instructions.md +40 -0
- package/templates/template/README.md +2 -2
- package/templates/template/ai-instructions.md +2 -2
package/dist/manifest.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare function validateManifest(raw: unknown): {
|
|
|
41
41
|
* else. It used to be inferred from the presence of `backend/src/index.ts`,
|
|
42
42
|
* which every scaffolded project had whether or not it wanted its own server, so
|
|
43
43
|
* projects predating the manifest silently landed on the custom runtime and paid
|
|
44
|
-
* for it
|
|
44
|
+
* for it.
|
|
45
45
|
*/
|
|
46
46
|
export declare function synthesizeManifest(projectRoot: string): RebaseProjectManifest;
|
|
47
47
|
export declare function manifestPath(projectRoot: string): string;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/** The dependency blocks a pin is moved in. `peerDependencies` is not one. */
|
|
2
|
+
export declare const PIN_FIELDS: readonly ["dependencies", "devDependencies", "optionalDependencies"];
|
|
3
|
+
export type PinField = typeof PIN_FIELDS[number];
|
|
4
|
+
/** Every framework package lives under this scope. */
|
|
5
|
+
export declare const FRAMEWORK_SCOPE = "@rebasepro/";
|
|
6
|
+
/** The package whose published versions stand for the release as a whole. */
|
|
7
|
+
export declare const RELEASE_PACKAGE = "@rebasepro/cli";
|
|
8
|
+
/** Whether `value` is one exact version — `0.21.0`, `0.21.1-canary.g8c5a265`. */
|
|
9
|
+
export declare function isExactVersion(value: string): boolean;
|
|
10
|
+
/** An error that carries the `--json` envelope's code and a remedy. */
|
|
11
|
+
export declare class UpgradeError extends Error {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
readonly hint?: string | undefined;
|
|
14
|
+
constructor(message: string, code: string, hint?: string | undefined);
|
|
15
|
+
}
|
|
16
|
+
export type SpecClass =
|
|
17
|
+
/** A plain version, optionally `^` or `~`: moved, keeping the prefix. */
|
|
18
|
+
{
|
|
19
|
+
kind: "movable";
|
|
20
|
+
prefix: "" | "^" | "~";
|
|
21
|
+
version: string;
|
|
22
|
+
}
|
|
23
|
+
/** A path on this machine. As a pin it is left alone; as an override it wins over every pin. */
|
|
24
|
+
| {
|
|
25
|
+
kind: "local";
|
|
26
|
+
protocol: "link:" | "file:" | "portal:";
|
|
27
|
+
}
|
|
28
|
+
/** Anything else, with the reason it is left alone. */
|
|
29
|
+
| {
|
|
30
|
+
kind: "other";
|
|
31
|
+
reason: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* What a dependency spec is, as far as moving it goes.
|
|
35
|
+
*
|
|
36
|
+
* Only a plain version is moved, because only a plain version has an obvious
|
|
37
|
+
* answer: `^0.19.1` becomes `^0.21.0`. A `>=` floor, a `||` union or a partial
|
|
38
|
+
* `^0.19` could each be rewritten several ways, and a tag or a workspace link
|
|
39
|
+
* already means something other than "this release" — so each is reported with
|
|
40
|
+
* its reason and left as written.
|
|
41
|
+
*/
|
|
42
|
+
export declare function classifySpec(spec: string): SpecClass;
|
|
43
|
+
/**
|
|
44
|
+
* The `@rebasepro/*` package an override key targets, or null.
|
|
45
|
+
*
|
|
46
|
+
* pnpm and npm key an override by name, optionally narrowed: `@rebasepro/x@<1`,
|
|
47
|
+
* `parent>@rebasepro/x`; yarn's `resolutions` by a path, `**\/@rebasepro/x`. The
|
|
48
|
+
* target is the last segment either way.
|
|
49
|
+
*/
|
|
50
|
+
export declare function overrideTarget(key: string): string | null;
|
|
51
|
+
export interface ProjectFiles {
|
|
52
|
+
/** Absolute paths, in a stable order. */
|
|
53
|
+
packageJsons: string[];
|
|
54
|
+
workspaceYamls: string[];
|
|
55
|
+
}
|
|
56
|
+
/** Every `package.json` and `pnpm-workspace.yaml` under the project root. */
|
|
57
|
+
export declare function discoverProjectFiles(projectRoot: string): ProjectFiles;
|
|
58
|
+
export interface ChangedPin {
|
|
59
|
+
/** Relative to the project root, POSIX separators. */
|
|
60
|
+
file: string;
|
|
61
|
+
name: string;
|
|
62
|
+
field: PinField;
|
|
63
|
+
from: string;
|
|
64
|
+
to: string;
|
|
65
|
+
}
|
|
66
|
+
export interface SkippedSpec {
|
|
67
|
+
file: string;
|
|
68
|
+
name: string;
|
|
69
|
+
/** A dependency block, or the override block the entry sits in. */
|
|
70
|
+
field: string;
|
|
71
|
+
spec: string;
|
|
72
|
+
reason: string;
|
|
73
|
+
}
|
|
74
|
+
export type OverrideAction = "bumped" | "kept-local" | "removed-local";
|
|
75
|
+
export interface OverrideFinding {
|
|
76
|
+
file: string;
|
|
77
|
+
/** The override key as written — `@rebasepro/types`, or a narrowed one. */
|
|
78
|
+
name: string;
|
|
79
|
+
spec: string;
|
|
80
|
+
action: OverrideAction;
|
|
81
|
+
/** The new value, for a bumped override. */
|
|
82
|
+
to?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface UnreadableFile {
|
|
85
|
+
file: string;
|
|
86
|
+
reason: string;
|
|
87
|
+
}
|
|
88
|
+
export interface UpgradePlan {
|
|
89
|
+
target: string;
|
|
90
|
+
changed: ChangedPin[];
|
|
91
|
+
skipped: SkippedSpec[];
|
|
92
|
+
overrides: OverrideFinding[];
|
|
93
|
+
unreadable: UnreadableFile[];
|
|
94
|
+
/** The files whose content changes, with the new content. */
|
|
95
|
+
writes: Array<{
|
|
96
|
+
file: string;
|
|
97
|
+
absolute: string;
|
|
98
|
+
content: string;
|
|
99
|
+
}>;
|
|
100
|
+
}
|
|
101
|
+
export interface PlanOptions {
|
|
102
|
+
/** Remove `link:`/`file:` overrides of framework packages instead of reporting them. */
|
|
103
|
+
dropLocalOverrides?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Work out every change the upgrade makes, without writing anything.
|
|
107
|
+
*
|
|
108
|
+
* `--dry-run` prints this; a real run writes `writes` and nothing else.
|
|
109
|
+
*/
|
|
110
|
+
export declare function planUpgrade(projectRoot: string, target: string, options?: PlanOptions): UpgradePlan;
|
|
111
|
+
/** Write what the plan changes. Nothing else on disk is touched. */
|
|
112
|
+
export declare function applyUpgradePlan(plan: UpgradePlan): void;
|
|
113
|
+
export type Installer = "pnpm" | "npm" | "yarn" | "bun";
|
|
114
|
+
/**
|
|
115
|
+
* The package manager this project installs with, from what is on disk.
|
|
116
|
+
*
|
|
117
|
+
* A lockfile is the project's own statement, so it wins. The search starts at
|
|
118
|
+
* the project root and walks up, because a project inside a workspace has its
|
|
119
|
+
* lockfile at the workspace root; it stops at the repository's root, so a stray
|
|
120
|
+
* lockfile in a home directory is never read as this project's choice. With no
|
|
121
|
+
* lockfile anywhere, a `pnpm-workspace.yaml` still says pnpm; otherwise npm,
|
|
122
|
+
* the one every Node install has.
|
|
123
|
+
*/
|
|
124
|
+
export declare function detectInstaller(projectRoot: string): Installer;
|
|
125
|
+
/**
|
|
126
|
+
* The install command for each package manager — pnpm's and npm's from the
|
|
127
|
+
* helpers every other command uses, yarn's and bun's spelled the same way.
|
|
128
|
+
*/
|
|
129
|
+
export declare function installCommand(installer: Installer): [string, string[]];
|
|
130
|
+
/**
|
|
131
|
+
* The exact version `--to` names.
|
|
132
|
+
*
|
|
133
|
+
* An exact version is used as written, with no network call: the control plane
|
|
134
|
+
* passes one when it rebuilds a project, and that rebuild must not depend on a
|
|
135
|
+
* registry lookup succeeding for anything but the install itself. Anything
|
|
136
|
+
* else is a dist-tag, asked of the registry through `npmView` — which runs npm
|
|
137
|
+
* in the project, so its `.npmrc` and the user's registry config apply.
|
|
138
|
+
*/
|
|
139
|
+
export declare function resolveTarget(requested: string, npmView: (spec: string) => Promise<string>): Promise<string>;
|
|
140
|
+
/**
|
|
141
|
+
* Whether moving from `from` to `to` goes backwards, for the one-word note the
|
|
142
|
+
* summary prints. Prerelease identifiers compare as SemVer orders them: a
|
|
143
|
+
* release outranks its own prereleases.
|
|
144
|
+
*/
|
|
145
|
+
export declare function isDowngrade(from: string, to: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"description": "Developer tools for Rebase projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"inquirer": "14.0.2",
|
|
44
44
|
"jiti": "^2.7.0",
|
|
45
45
|
"pg": "^8.22.0",
|
|
46
|
-
"@rebasepro/agent-skills": "0.21.
|
|
47
|
-
"@rebasepro/client": "0.21.
|
|
48
|
-
"@rebasepro/
|
|
49
|
-
"@rebasepro/
|
|
50
|
-
"@rebasepro/types": "0.21.
|
|
51
|
-
"@rebasepro/server-postgres": "0.21.
|
|
46
|
+
"@rebasepro/agent-skills": "0.21.1",
|
|
47
|
+
"@rebasepro/client": "0.21.1",
|
|
48
|
+
"@rebasepro/server": "0.21.1",
|
|
49
|
+
"@rebasepro/codegen": "0.21.1",
|
|
50
|
+
"@rebasepro/types": "0.21.1",
|
|
51
|
+
"@rebasepro/server-postgres": "0.21.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.1.2",
|
|
@@ -9,9 +9,11 @@ the endpoints change with it.
|
|
|
9
9
|
|
|
10
10
|
## Serving a table
|
|
11
11
|
|
|
12
|
-
A table is served once it has an authorization model: row-level security
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
A table is served once it has an authorization model: row-level security
|
|
13
|
+
enabled. Until then the server skips it — deliberately, so a new table is never
|
|
14
|
+
exposed just by existing — and logs each table it skipped and why. A table with
|
|
15
|
+
row-level security on but no policy yet is served and returns no rows (the boot
|
|
16
|
+
log names it); a policy is what opens it:
|
|
15
17
|
|
|
16
18
|
```sql
|
|
17
19
|
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Rebase AI Coding Rules
|
|
2
|
+
|
|
3
|
+
This is a **headless** Rebase project: a REST API, auth, storage and realtime over your PostgreSQL database, with no admin panel. There are no collection files and no generated schema — the server reads the database schema at boot and serves each table that has row-level security enabled. `README.md` shows the SQL.
|
|
4
|
+
|
|
5
|
+
`.mcp.json` in this directory wires the Rebase MCP server up already — no login, no token: it reads `.rebase/state.json` while `pnpm dev` is running. For the full skills (auth, RLS, functions, storage, deployment), run `pnpm skills:install`, or `rebase skills install --agent <claude|cursor|windsurf|gemini|codex|kiro|copilot>` to pick one.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
| Command | What it does |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `pnpm dev` | The backend and the managed development database, together |
|
|
12
|
+
| `rebase db url` | Print the connection string in use — it pipes straight into `psql` |
|
|
13
|
+
| `pnpm db:migrate` | Apply the migration files in `backend/drizzle/migrations`. Needs your own PostgreSQL, not the managed development database |
|
|
14
|
+
| `pnpm schema:introspect` | Existing tables → collection definitions in `config/collections/`. That directory ends headless mode; see rule 1 |
|
|
15
|
+
| `pnpm generate:sdk` | Regenerate the typed client from `config/collections/` — so in a headless project it has nothing to read until you introspect |
|
|
16
|
+
| `pnpm build` then `pnpm start` | Build the deployable bundle, then run it |
|
|
17
|
+
| `pnpm skills:install` | Install the Rebase skills for your assistant |
|
|
18
|
+
| `pnpm example` | Run `scripts/example.ts` against the running backend — the SDK, end to end |
|
|
19
|
+
| `rebase doctor` | Checks the environment and the database connection — run this before guessing |
|
|
20
|
+
| `rebase resources --write` | After declaring a database, bucket or topic in `config/resources.ts`: regenerate `rebase.resources.json` and commit it (`pnpm build` does this too) |
|
|
21
|
+
| `pnpm deploy` | Deploys this project. Never run it; see below |
|
|
22
|
+
|
|
23
|
+
## Never
|
|
24
|
+
|
|
25
|
+
- **Never deploy.** `pnpm deploy`, `rebase cloud deploy`, `firebase deploy`, `gcloud run deploy` — print the command and let the human run it, even when the task list ends in "deploy" and the tests are green.
|
|
26
|
+
- **Never edit `.env`.** It holds generated secrets and the connection string. Add a variable by asking, and document it in `.env.example`.
|
|
27
|
+
- **Never edit `rebase.resources.json`.** It is generated from `config/resources.ts` — declare there, then `rebase resources --write`.
|
|
28
|
+
- **Never pass `--allow-destructive`** to anything pointed at a database that is not the local development one. It drops columns and tables.
|
|
29
|
+
- **Never read or write application rows with raw SQL or Drizzle from code.** The schema is SQL here; the data is not. That path skips validation, hooks and row-level security.
|
|
30
|
+
|
|
31
|
+
## Core rules
|
|
32
|
+
|
|
33
|
+
1. **The database is the schema.** Change it with SQL — `rebase db url` gives `psql` the connection string — or with migration files, then restart `pnpm dev`: the server reads the tables at boot. Do not create `config/collections/` to describe a table; a project with collection files serves those instead of reading the database, which is a different project (`README.md`, "Adding an admin UI later").
|
|
34
|
+
2. **A table is served only with row-level security enabled.** Until then the server skips it and logs why — deliberately, so a table is never exposed just by existing — and with RLS on but no policy it is served and returns no rows. The policy *is* the authorization model: write the rule that says whose row it is, using the `rebase` schema's SQL functions `uid()`, `roles()` and `jwt()`, never a policy that admits everyone just to make rows appear.
|
|
35
|
+
3. **Storage is not under row-level security.** `config/storage.ts` exports `storageAuthorize`, the whole access model for files, and a production deployment with file storage enabled refuses to boot without one. Change the rule there to match your application; do not delete it.
|
|
36
|
+
4. **Use the SDK**: server-side that is `rebase.dataAsAdmin.<table>` for work done as the service identity, or `getDriver(c)` inside a function when the read should run as the caller. The server client has no plain `data` accessor — it is omitted precisely so that the choice of identity is written down.
|
|
37
|
+
5. **Guard every custom route**: routes in `backend/functions/` are mounted **without** an auth requirement — webhook receivers need that — so each one is public until you guard it. Import `requireAuth` / `requireAdmin` from `@rebasepro/server/functions` and pass them in the route's own middleware slot (`app.post("/", requireAuth, handler)`), not via `app.use()`, which only covers routes declared below it. Reading `getUser(c)` is not a guard: an anonymous caller gets `undefined` and the handler still runs. See `backend/functions/hello.ts` for all three tiers.
|
|
38
|
+
6. **In `backend/functions/`, always import from `@rebasepro/server/functions`** — never from `@rebasepro/server`. Both work today; the subpath is the portable one, and it also gives you the typed context accessors (`getUser`, `getDriver`, `requireDriver`) instead of casting `c.get("user")`. The package root is for a server entrypoint, not for route handlers.
|
|
39
|
+
7. **Never read `process.env` at the top of a function file.** A module-scope read that comes back undefined throws at import time, and the loader reports that as a *skipped function* — the route simply 404s with no error attached to it. Read configuration inside the handler with `requireEnv(c, "NAME")`, or build a client once with `lazyResource(env => new Client(env.KEY))`.
|
|
40
|
+
8. **Work that outlives the response goes in `waitUntil(c, promise)`**, not a floating promise. A floating promise is dropped when the process shuts down mid-deploy; `waitUntil` is what a graceful shutdown waits for.
|
|
@@ -34,8 +34,8 @@ pnpm dev # or: npm run dev
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
That is the whole first run. `rebase dev` generates the Drizzle schema from
|
|
37
|
-
`config/collections`, starts the database, creates
|
|
38
|
-
|
|
37
|
+
`config/collections`, starts the database, creates a table for every collection
|
|
38
|
+
declared there, and serves the API and the admin panel.
|
|
39
39
|
|
|
40
40
|
`rebase dev` prints the two URLs it actually bound. The first account you
|
|
41
41
|
register becomes the admin. `REBASE_ADMIN_EMAIL` and `REBASE_ADMIN_PASSWORD` in
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rebase AI Coding Rules
|
|
2
2
|
|
|
3
|
-
`.mcp.json` in this directory wires the Rebase MCP server up already — no login, no token: it reads `.rebase/state.json` while `pnpm dev` is running. For the full skills (collections, auth, RLS, deployment, the UI kit), run `pnpm skills:install`, or `rebase skills install --agent <claude|cursor|windsurf|gemini>` to pick one.
|
|
3
|
+
`.mcp.json` in this directory wires the Rebase MCP server up already — no login, no token: it reads `.rebase/state.json` while `pnpm dev` is running. For the full skills (collections, auth, RLS, deployment, the UI kit), run `pnpm skills:install`, or `rebase skills install --agent <claude|cursor|windsurf|gemini|codex|kiro|copilot>` to pick one.
|
|
4
4
|
|
|
5
5
|
## Commands
|
|
6
6
|
|
|
@@ -36,4 +36,4 @@
|
|
|
36
36
|
5. **In `backend/functions/`, always import from `@rebasepro/server/functions`** — never from `@rebasepro/server`. Both work today; the subpath is the portable one, and it also gives you the typed context accessors (`getUser`, `getDriver`, `requireDriver`) instead of casting `c.get("user")`. The package root is for a server entrypoint, not for route handlers.
|
|
37
37
|
6. **Never read `process.env` at the top of a function file.** A module-scope read that comes back undefined throws at import time, and the loader reports that as a *skipped function* — the route simply 404s with no error attached to it. Read configuration inside the handler with `requireEnv(c, "NAME")`, or build a client once with `lazyResource(env => new Client(env.KEY))`.
|
|
38
38
|
7. **Work that outlives the response goes in `waitUntil(c, promise)`**, not a floating promise. A floating promise is dropped when the process shuts down mid-deploy; `waitUntil` is what a graceful shutdown waits for.
|
|
39
|
-
8. **Build UI from the kit, never from scratch**: any custom view, home page, dashboard or entity tab must be composed from `@rebasepro/ui` components (`Card`, `Typography`, `Button`, `Chip`, `Alert`, …) and the theme's
|
|
39
|
+
8. **Build UI from the kit, never from scratch**: any custom view, home page, dashboard or entity tab must be composed from `@rebasepro/ui` components (`Card`, `Typography`, `Button`, `Chip`, `Alert`, …) and the theme's tokens. Backgrounds and borders take a surface by role — `bg-surface-frame`, `bg-surface-sheet`, `bg-surface-card`, `bg-surface-raised`, `bg-surface-field`, `bg-surface-well`, `border-hairline` — and those switch with the theme on their own, so they need no `dark:` pair; text takes `<Typography>` or `text-text-primary dark:text-text-primary-dark`. Do **not** invent a palette, a type scale, or hand-written CSS: a hardcoded colour like `#111` is invisible in one of the two themes and nothing will catch it. The roles and their reasoning are in `@rebasepro/ui/theme.css` in your `node_modules`; the live reference is `import { UIReferenceView } from "@rebasepro/app/debug"` — mount it on a route and look before building a view. See [Styling Custom UI](https://rebase.pro/docs/frontend/styling).
|