@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
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
# Rebase AI Rules
|
|
2
|
-
Please refer to and follow the instructions defined in [ai-instructions.md](ai-instructions.md).
|
|
2
|
+
Please refer to and follow the instructions defined in [ai-instructions.md](./ai-instructions.md).
|
|
3
|
+
Install the Rebase skills for this assistant: `rebase skills install --agent claude`.
|
|
@@ -12,10 +12,12 @@ point for this project.
|
|
|
12
12
|
|
|
13
13
|
### Prerequisites
|
|
14
14
|
|
|
15
|
-
- [Node.js](https://nodejs.org) >=
|
|
15
|
+
- [Node.js](https://nodejs.org) >= 22.22
|
|
16
16
|
- [pnpm](https://pnpm.io) or [npm](https://www.npmjs.com) (v7+)
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
That is the whole list. There is no database to install: with no `DATABASE_URL`
|
|
19
|
+
set, `rebase dev` starts a managed PostgreSQL (PGlite) inside this project, and
|
|
20
|
+
the runtime creates your collections' tables at boot.
|
|
19
21
|
|
|
20
22
|
### Run it
|
|
21
23
|
|
|
@@ -25,26 +27,20 @@ point for this project.
|
|
|
25
27
|
pnpm install # or: npm install
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
2. Start the
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
docker compose up -d db
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
3. Create the database tables from your collections:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
pnpm run db:push # or: npm run db:push
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
4. Start the dev servers:
|
|
30
|
+
2. Start the dev servers:
|
|
41
31
|
|
|
42
32
|
```bash
|
|
43
33
|
pnpm dev # or: npm run dev
|
|
44
34
|
```
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
That is the whole first run. `rebase dev` generates the Drizzle schema from
|
|
37
|
+
`config/collections`, starts the database, creates the tables — including the
|
|
38
|
+
example `posts`, `authors` and `tags` — and serves the API and the admin panel.
|
|
39
|
+
|
|
40
|
+
`rebase dev` prints the two URLs it actually bound. The first account you
|
|
41
|
+
register becomes the admin. `REBASE_ADMIN_EMAIL` and `REBASE_ADMIN_PASSWORD` in
|
|
42
|
+
`.env` are ignored here and read by the production boot below, where that window
|
|
43
|
+
is shut — see [Self-hosting](#self-hosting-with-docker).
|
|
48
44
|
|
|
49
45
|
Ports are **derived from this project's path**, not fixed at 3001/5173, so
|
|
50
46
|
several Rebase projects can run at once without colliding. That is why the URL
|
|
@@ -52,10 +48,30 @@ in your terminal is the one to trust — and why the `PORT` and `VITE_API_URL` i
|
|
|
52
48
|
`.env` are ignored by `rebase dev` (they apply to `rebase start`, the production
|
|
53
49
|
server). Pin a port with `rebase dev --port 3001` if you need a stable one.
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
### Variant: your own PostgreSQL
|
|
52
|
+
|
|
53
|
+
Uncomment `DATABASE_URL` in `.env` and run `pnpm dev` again. Nothing else
|
|
54
|
+
changes. A `DATABASE_URL` that is already set is never touched.
|
|
55
|
+
|
|
56
|
+
With your own database you also get the migration commands, which the managed
|
|
57
|
+
one cannot offer — they plan changes with Atlas, which needs a second empty
|
|
58
|
+
database to compare against, and PGlite serves exactly one:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm run db:push
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Boot already creates missing tables and columns additively, so `db push` is for
|
|
65
|
+
what it deliberately leaves alone: junction-table RLS on many-to-many relations,
|
|
66
|
+
and any change that is not purely additive — a renamed column, a narrowed type,
|
|
67
|
+
a removed field.
|
|
68
|
+
|
|
69
|
+
The `docker-compose.yml` in this project runs PostgreSQL in a container if you
|
|
70
|
+
would rather not install one:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docker compose up -d db
|
|
74
|
+
```
|
|
59
75
|
|
|
60
76
|
## Project Structure
|
|
61
77
|
|
|
@@ -104,7 +120,9 @@ export default defineFunction((app) => {
|
|
|
104
120
|
});
|
|
105
121
|
```
|
|
106
122
|
|
|
107
|
-
Call from the client SDK: `client.
|
|
123
|
+
Call from the client SDK: `client.functions.invoke("hello", { name: "World" })`.
|
|
124
|
+
It sends POST by default — for the GET route above, pass
|
|
125
|
+
`{ method: "GET" }` as the third argument.
|
|
108
126
|
|
|
109
127
|
**Functions are not authenticated for you.** The functions router parses the
|
|
110
128
|
caller's token into the request context but does not reject anonymous requests —
|
|
@@ -122,7 +140,7 @@ Collections are defined once in `config/collections/` and used by both the front
|
|
|
122
140
|
|
|
123
141
|
All configuration is managed through a single `.env` file in the project root. Both the backend and frontend read from this file:
|
|
124
142
|
|
|
125
|
-
- **Backend**: loads
|
|
143
|
+
- **Backend**: the runtime loads the project root's `.env` before it boots
|
|
126
144
|
- **Frontend**: Vite reads `VITE_*` variables via `envDir` pointing to the project root
|
|
127
145
|
- **Scripts**: load via `dotenv` from the project root
|
|
128
146
|
|
|
@@ -133,15 +151,20 @@ All configuration is managed through a single `.env` file in the project root. B
|
|
|
133
151
|
Two containers: PostgreSQL, and the Rebase runtime with your built project
|
|
134
152
|
mounted into it. There is no application image to build.
|
|
135
153
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
push the schema once, while the database is up.
|
|
154
|
+
This is the [variant above](#variant-your-own-postgresql), packaged: the
|
|
155
|
+
compose database is a database of your own, so step 0 is the same one line.
|
|
139
156
|
|
|
140
157
|
```bash
|
|
158
|
+
# 0. Point the project at the compose database: uncomment DATABASE_URL in .env
|
|
159
|
+
# (the line whose host is 127.0.0.1 and whose password matches
|
|
160
|
+
# DATABASE_PASSWORD — `rebase init` wrote it there, commented out)
|
|
161
|
+
|
|
141
162
|
# 1. Build your project into ./dist-bundle
|
|
142
163
|
pnpm run build # or: npm run build
|
|
143
164
|
|
|
144
|
-
# 2. Start the database
|
|
165
|
+
# 2. Start the database. Boot creates the tables from your collections;
|
|
166
|
+
# `db:push` is for what it leaves alone — junction-table RLS, and any
|
|
167
|
+
# change that is not purely additive.
|
|
145
168
|
docker compose up -d db
|
|
146
169
|
pnpm run db:push # or: npm run db:push
|
|
147
170
|
|
|
@@ -161,6 +184,14 @@ docker compose down -v
|
|
|
161
184
|
One container now serves the API at `/api` and the admin at `/` — same origin,
|
|
162
185
|
so there is no CORS between them and no second web server to run.
|
|
163
186
|
|
|
187
|
+
Sign in as `REBASE_ADMIN_EMAIL` with `REBASE_ADMIN_PASSWORD`, both in `.env`.
|
|
188
|
+
This stack runs with `NODE_ENV=production`, where the first-account-becomes-admin
|
|
189
|
+
window is shut — it has to be, because the container answers on a hostname
|
|
190
|
+
before you have typed anything, so whoever reached the sign-up form first would
|
|
191
|
+
own the deployment. The runtime creates that one account while the user table is
|
|
192
|
+
empty and never again. Change the email to yours before the first boot, and
|
|
193
|
+
change the password after signing in.
|
|
194
|
+
|
|
164
195
|
To upgrade Rebase, set `REBASE_VERSION` in `.env` and restart. Your bundle is
|
|
165
196
|
untouched.
|
|
166
197
|
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
# Rebase AI Coding Rules
|
|
2
2
|
|
|
3
|
-
Rebase
|
|
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.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Commands
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
| Command | What it does |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `pnpm dev` | Backend, frontend and the managed development database, together |
|
|
10
|
+
| `pnpm schema:generate` then `pnpm db:push` | The same loop with `pnpm dev` not running, plus the changes boot leaves alone: collections → Drizzle schema → development database |
|
|
11
|
+
| `pnpm db:generate` then `pnpm db:migrate` | The production path — write the migration file, then apply it |
|
|
12
|
+
| `pnpm schema:introspect` | The other direction: existing tables → collection definitions |
|
|
13
|
+
| `pnpm generate:sdk` | Regenerate the typed client from the collections |
|
|
14
|
+
| `pnpm build` then `pnpm start` | Build the deployable bundle, then run it |
|
|
15
|
+
| `pnpm skills:install` | Install the Rebase skills for your assistant |
|
|
16
|
+
| `pnpm example` | Run `scripts/example.ts` against the running backend — the SDK, end to end |
|
|
17
|
+
| `rebase doctor` | What disagrees between collections, generated schema and the live database — run this before guessing |
|
|
18
|
+
| `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) |
|
|
19
|
+
| `pnpm deploy` | Deploys this project. Never run it; see below |
|
|
10
20
|
|
|
11
|
-
##
|
|
21
|
+
## Never
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
- **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.
|
|
24
|
+
- **Never edit `.env`.** It holds generated secrets and the connection string. Add a variable by asking, and document it in `.env.example`.
|
|
25
|
+
- **Never edit `backend/src/schema.generated.ts`.** It is overwritten by `pnpm schema:generate`; the collection file is the source.
|
|
26
|
+
- **Never edit `rebase.resources.json`.** It is generated from `config/resources.ts` — declare there, then `rebase resources --write`.
|
|
27
|
+
- **Never pass `--allow-destructive`** to anything pointed at a database that is not the local development one. It drops columns and tables.
|
|
28
|
+
- **Never write raw SQL or Drizzle queries against application tables.** That path skips validation, hooks and row-level security.
|
|
29
|
+
|
|
30
|
+
## Core rules
|
|
31
|
+
|
|
32
|
+
1. **Schema-as-code**: define and edit collections in `config/collections/` with `defineCollection` from `@rebasepro/cms-types`, and register each one in that directory's `index.ts` — a collection missing from the barrel does not exist. Never hand-edit generated schemas or Postgres tables.
|
|
33
|
+
2. **Applying a collection change**: while `pnpm dev` is running, saving a file in `config/collections/` is the whole step — it regenerates `backend/src/schema.generated.ts`, restarts the backend, and boot creates the tables and columns that are missing. With `pnpm dev` stopped, that is `pnpm schema:generate`. `pnpm db:push` is only for what boot deliberately leaves alone — a renamed column, a narrowed type, a removed field, junction-table RLS — and it needs your own PostgreSQL, not the managed development database. In production the pair is `pnpm db:generate` then `pnpm db:migrate`.
|
|
34
|
+
3. **Use the SDK**: server-side that is `rebase.dataAsAdmin.<slug>` 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.
|
|
18
35
|
4. **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.
|
|
19
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.
|
|
20
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))`.
|
|
21
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.
|
|
22
|
-
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 colour tokens (`text-surface-*`, `bg-surface-accent-*`, `text-primary`), with a `dark:` value beside every light one. 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 live reference ships in your `node_modules` — read `@rebasepro/app/src/components/Debug/UIReferenceView.tsx` before building a view, and see [Styling Custom UI](https://rebase.pro/docs/frontend/styling).
|
|
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 colour tokens (`text-surface-*`, `bg-surface-accent-*`, `text-primary`), with a `dark:` value beside every light one. 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 live reference ships in your `node_modules` — read `@rebasepro/app/src/components/Debug/UIReferenceView.tsx` before building a view, and see [Styling Custom UI](https://rebase.pro/docs/frontend/styling).
|
|
@@ -15,7 +15,11 @@ import {
|
|
|
15
15
|
* GET /api/functions/hello/stats (admins only)
|
|
16
16
|
*
|
|
17
17
|
* Call from the client SDK:
|
|
18
|
-
* const result = await client.
|
|
18
|
+
* const result = await client.functions.invoke("hello", { name: "World" });
|
|
19
|
+
*
|
|
20
|
+
* `invoke` sends POST by default, which is why the POST route below is the one
|
|
21
|
+
* that takes a body. For the public GET above, pass the method:
|
|
22
|
+
* await client.functions.invoke("hello", undefined, { method: "GET" });
|
|
19
23
|
*
|
|
20
24
|
* Authored with `defineFunction`, which hands you a pre-typed Hono app (so
|
|
21
25
|
* `c.get("user")` / `c.get("driver")` are typed) and the `rebase` singleton via
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
"@hono/node-server": "^2.0.12",
|
|
18
18
|
"pg": "^8.11.3",
|
|
19
19
|
"ws": "^8.16.0",
|
|
20
|
-
"dotenv": "^
|
|
20
|
+
"dotenv": "^17.4.2",
|
|
21
21
|
"zod": "^4.4.3",
|
|
22
22
|
"ts-morph": "28.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/pg": "^8.6.5",
|
|
26
|
-
"@types/node": "^
|
|
26
|
+
"@types/node": "^22.19.4",
|
|
27
27
|
"@types/ws": "^8.5.10",
|
|
28
28
|
"tsx": "^4.20.6",
|
|
29
29
|
"typescript": "^5.9.2"
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
// This file is
|
|
2
|
-
// Do not edit manually.
|
|
1
|
+
// This file is generated from config/collections/. Do not edit manually.
|
|
3
2
|
//
|
|
4
|
-
//
|
|
3
|
+
// `rebase dev` regenerates it before it starts anything, so the stub below is
|
|
4
|
+
// replaced on your first run and you should not have to think about this file.
|
|
5
|
+
// To regenerate it by hand: `pnpm run schema:generate`.
|
|
6
|
+
//
|
|
7
|
+
// Not `db:generate` — that one plans an Atlas migration and needs a real
|
|
8
|
+
// PostgreSQL. `schema:generate` only reads your collections and writes this
|
|
9
|
+
// file, so it works on any database, including the managed development one.
|
|
5
10
|
|
|
6
11
|
export const tables = {};
|
|
7
12
|
export const enums = {};
|
|
@@ -7,3 +7,9 @@
|
|
|
7
7
|
//
|
|
8
8
|
// A BaaS project has no admin panel and no reason for this file. Without it, `admin` on
|
|
9
9
|
// a collection or a property is a type error, which is the guarantee.
|
|
10
|
+
//
|
|
11
|
+
// It is only needed by a file that annotates a collection with the *type* —
|
|
12
|
+
// `const posts: PostgresCollectionConfig = { … }`. Every collection here is written with
|
|
13
|
+
// `defineCollection()` instead, which is imported from `@rebasepro/cms-types` and brings
|
|
14
|
+
// the augmentation with it. The reference stays because it costs one line and covers the
|
|
15
|
+
// case where somebody reaches for the annotation.
|
|
@@ -35,17 +35,31 @@ export const main = database();
|
|
|
35
35
|
* Object storage.
|
|
36
36
|
*
|
|
37
37
|
* Uncomment to give this project a bucket. The default-keyed one binds from the
|
|
38
|
-
* plain `S3_BUCKET` / `GCS_BUCKET
|
|
39
|
-
*
|
|
38
|
+
* plain `S3_BUCKET` / `GCS_BUCKET`; a named one appends its key, so
|
|
39
|
+
* `bucket("media")` reads `S3_BUCKET__MEDIA`. In development an unbound bucket
|
|
40
|
+
* is a local directory until you bind it; in production it is off, and
|
|
41
|
+
* `rebase status` tells you which variable it is waiting on.
|
|
40
42
|
*
|
|
41
43
|
* export const files = bucket({ engine: "s3" });
|
|
42
44
|
* export const media = bucket("media", { engine: "s3" });
|
|
43
45
|
*
|
|
46
|
+
* One of them has to be the one an upload reaches when the property names no
|
|
47
|
+
* `storageSource` — the default-keyed `bucket()` above, or a named bucket
|
|
48
|
+
* marked `bucket("media", { engine: "s3", default: true })`. A project of named
|
|
49
|
+
* buckets with no default refuses to boot rather than have one chosen for it:
|
|
50
|
+
* that choice used to be made by declaration order, and it came out differently
|
|
51
|
+
* in development and in production.
|
|
52
|
+
*
|
|
53
|
+
* Point a property at it by handle — the same name, spelled once:
|
|
54
|
+
*
|
|
55
|
+
* import { media } from "../resources";
|
|
56
|
+
* cover: { type: "string", storage: { storageSource: media } }
|
|
57
|
+
*
|
|
44
58
|
* ## When you have several on one provider
|
|
45
59
|
*
|
|
46
60
|
* Name an `account` and they share one credential set instead of repeating it:
|
|
47
61
|
*
|
|
48
|
-
* export const media = bucket("media", { engine: "s3", account: "minio" });
|
|
62
|
+
* export const media = bucket("media", { engine: "s3", account: "minio", default: true });
|
|
49
63
|
* export const avatars = bucket("avatars", { engine: "s3", account: "minio" });
|
|
50
64
|
*
|
|
51
65
|
* That reads `S3_BUCKET__MEDIA` and `S3_BUCKET__AVATARS` — each bucket keeps its
|
|
@@ -55,3 +69,28 @@ export const main = database();
|
|
|
55
69
|
* one, so a single source can be moved to another provider without breaking the
|
|
56
70
|
* rest off their shared account.
|
|
57
71
|
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Background work.
|
|
75
|
+
*
|
|
76
|
+
* A topic fans out: every subscription gets each event, and each retries on
|
|
77
|
+
* its own. A queue is a work list with one handler. Both ride on the durable
|
|
78
|
+
* job queue in this project's database, so declaring either turns it on, and
|
|
79
|
+
* neither needs any environment configuration.
|
|
80
|
+
*
|
|
81
|
+
* Add them to the import above when you declare one — they are named here, not
|
|
82
|
+
* imported, because an import of a symbol nothing uses is an import that breaks
|
|
83
|
+
* the moment the pinned `@rebasepro/types` predates it:
|
|
84
|
+
*
|
|
85
|
+
* import { topic, queue } from "@rebasepro/types";
|
|
86
|
+
*
|
|
87
|
+
* export const signups = topic<{ userId: string }>("signups");
|
|
88
|
+
* signups.subscription("send-welcome", async ({ userId }) => { … });
|
|
89
|
+
*
|
|
90
|
+
* export const thumbnails = queue<{ key: string }>("thumbnails");
|
|
91
|
+
* thumbnails.handler(async ({ key }) => { … });
|
|
92
|
+
*
|
|
93
|
+
* Crons and functions are files: `backend/crons/<name>.ts` and
|
|
94
|
+
* `backend/functions/<name>.ts`. They appear in `rebase resources` under the
|
|
95
|
+
* name of the file.
|
|
96
|
+
*/
|
|
@@ -60,7 +60,8 @@ import { isPublicStoragePath, type StorageAuthorize } from "@rebasepro/types";
|
|
|
60
60
|
* `storagePublicRead: true` when the bucket genuinely is a public read-only CDN,
|
|
61
61
|
* or `storageInsecureAllowAnyAuthenticated: true` for a single-tenant app where
|
|
62
62
|
* every signed-in user is trusted with every file. Both are set on
|
|
63
|
-
* `initializeRebaseBackend
|
|
63
|
+
* `initializeRebaseBackend`, which this project does not call: the published
|
|
64
|
+
* runtime boots it. `rebase eject` writes that entry point if you want one.
|
|
64
65
|
*/
|
|
65
66
|
export const storageAuthorize: StorageAuthorize = ({ key, user, operation }) => {
|
|
66
67
|
// The `public/` prefix is the framework's convention for world-readable
|
|
@@ -12,14 +12,27 @@
|
|
|
12
12
|
#
|
|
13
13
|
# rebase build # produces ./dist-bundle
|
|
14
14
|
# docker compose up -d db
|
|
15
|
-
#
|
|
16
|
-
#
|
|
15
|
+
# docker compose up # boot creates the tables it is missing
|
|
16
|
+
#
|
|
17
|
+
# `rebase db push` is the separate step for what boot deliberately leaves
|
|
18
|
+
# alone: junction-table RLS, and any change that is not purely additive.
|
|
17
19
|
#
|
|
18
20
|
# One container then serves the API at /api and the admin at / — same origin, so
|
|
19
21
|
# there is no CORS to configure between them and no second web server.
|
|
20
22
|
#
|
|
21
23
|
# For development, use `rebase dev` instead.
|
|
22
24
|
#
|
|
25
|
+
# This file is the recommended one for a scaffolded project: it is in your
|
|
26
|
+
# repository, `rebase init` filled in its secrets, its first admin account and
|
|
27
|
+
# its pinned runtime version, and it is booted by the framework's own acceptance
|
|
28
|
+
# gate on every push. The copy in the Rebase repository
|
|
29
|
+
# (`infra/docker/docker-compose.selfhost.yml`) is the reference for a deployment
|
|
30
|
+
# with no scaffold behind it, and adds a pgbouncer profile and the split-process
|
|
31
|
+
# shapes. The two agree on every environment variable except the database
|
|
32
|
+
# password: this file reads DATABASE_PASSWORD, which `rebase init` also embeds
|
|
33
|
+
# in the DATABASE_URL in your .env; that one reads POSTGRES_PASSWORD, generated
|
|
34
|
+
# by its own quickstart script.
|
|
35
|
+
#
|
|
23
36
|
# To upgrade Rebase, change REBASE_VERSION and restart. Your bundle is untouched.
|
|
24
37
|
# To run your OWN server code instead, `rebase eject` — it writes the entrypoint,
|
|
25
38
|
# a Dockerfile and a compose file that builds them.
|
|
@@ -97,9 +110,35 @@ services:
|
|
|
97
110
|
# its allowed origins is one that eventually allows the wrong one.
|
|
98
111
|
CORS_ORIGINS: ${CORS_ORIGINS:?set CORS_ORIGINS to the origin you browse to, e.g. http://localhost:3001}
|
|
99
112
|
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
113
|
+
# ─── The first account ───────────────────────────────────────────────
|
|
114
|
+
#
|
|
115
|
+
# This service runs with NODE_ENV=production, and in production the
|
|
116
|
+
# first-registration-becomes-admin window is CLOSED. That window is right
|
|
117
|
+
# for a laptop, where the person at the keyboard is the operator, and
|
|
118
|
+
# wrong the moment this stack answers on a hostname: the container is
|
|
119
|
+
# serving before you have typed anything, so whoever reaches the sign-up
|
|
120
|
+
# form first would own the deployment.
|
|
121
|
+
#
|
|
122
|
+
# So the first admin is named here instead. The runtime creates it once,
|
|
123
|
+
# while the user table is empty, and does nothing on every boot after
|
|
124
|
+
# that — nothing is left to be claimed, and nothing to race for.
|
|
125
|
+
#
|
|
126
|
+
# `rebase init` wrote both into .env with a generated password. Change
|
|
127
|
+
# the email to yours, sign in, and change the password.
|
|
128
|
+
REBASE_ADMIN_EMAIL: ${REBASE_ADMIN_EMAIL:?set REBASE_ADMIN_EMAIL in .env — `rebase init` writes one}
|
|
129
|
+
REBASE_ADMIN_PASSWORD: ${REBASE_ADMIN_PASSWORD:?set REBASE_ADMIN_PASSWORD in .env — `rebase init` generates one, min 12 characters}
|
|
130
|
+
# Open sign-up instead: set this to false in .env and clear the two above
|
|
131
|
+
# — but then the first person to reach the form becomes the administrator.
|
|
132
|
+
DISABLE_SELF_REGISTRATION: ${DISABLE_SELF_REGISTRATION:-true}
|
|
133
|
+
|
|
134
|
+
# `ensure` (the default) creates missing tables and columns at boot —
|
|
135
|
+
# your collections' as well as auth's — and is additive only: nothing is
|
|
136
|
+
# dropped, narrowed or rewritten, because that must never happen as a
|
|
137
|
+
# side effect of a container restart.
|
|
138
|
+
#
|
|
139
|
+
# `rebase db push` is still what applies the rest: junction-table RLS on
|
|
140
|
+
# many-to-many relations, and any change that is not purely additive.
|
|
141
|
+
# Set this to `none` to take a deployment out of provisioning entirely.
|
|
103
142
|
REBASE_MIGRATE_ON_BOOT: ${REBASE_MIGRATE_ON_BOOT:-ensure}
|
|
104
143
|
|
|
105
144
|
# Uploads land on the named volume below, which survives restarts. That is
|
|
@@ -111,10 +150,12 @@ services:
|
|
|
111
150
|
STORAGE_PATH: /uploads
|
|
112
151
|
FORCE_LOCAL_STORAGE: "true"
|
|
113
152
|
volumes:
|
|
114
|
-
# Your built project.
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
-
|
|
153
|
+
# Your built project. `rebase build` installs its declared dependencies
|
|
154
|
+
# into the bundle by default, so `node_modules` is already there and the
|
|
155
|
+
# mount can be read-only — a compromised hook then cannot rewrite the code
|
|
156
|
+
# that runs after it. Drop the `:ro` if you build with `--no-vendor`: the
|
|
157
|
+
# runtime installs at every start instead, and needs to write here.
|
|
158
|
+
- ./dist-bundle:/bundle:ro
|
|
118
159
|
- uploads:/uploads
|
|
119
160
|
|
|
120
161
|
volumes:
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@rebasepro/app": "workspace:*",
|
|
8
|
-
"@rebasepro/plugin-ai": "workspace:*",
|
|
9
8
|
"@rebasepro/client": "workspace:*",
|
|
10
9
|
"@rebasepro/cms": "workspace:*",
|
|
11
10
|
"@rebasepro/studio": "workspace:*",
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
"devDependencies": {
|
|
41
40
|
"@tailwindcss/typography": "^0.5.16",
|
|
42
41
|
"@tailwindcss/vite": "^4.1.12",
|
|
43
|
-
"@types/node": "^
|
|
42
|
+
"@types/node": "^22.19.4",
|
|
44
43
|
"@types/react": "^19.0.8",
|
|
45
44
|
"@types/react-dom": "^19.0.3",
|
|
46
45
|
"@vitejs/plugin-react": "^4.4.1",
|
|
@@ -11,8 +11,12 @@ import { RebaseStudio } from "@rebasepro/studio";
|
|
|
11
11
|
import { createRebaseClient } from "@rebasepro/client";
|
|
12
12
|
import { collections } from "virtual:rebase-collections";
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
|
|
14
|
+
// `rebase dev` injects VITE_API_URL with the port it actually bound, and that
|
|
15
|
+
// port is derived from this project's path rather than fixed — so a
|
|
16
|
+
// `http://localhost:3001` fallback here names a port nothing is listening on.
|
|
17
|
+
// A deployed build serves the admin from the same origin as the API, where an
|
|
18
|
+
// empty value is exactly what you want.
|
|
19
|
+
const API_URL = import.meta.env.VITE_API_URL;
|
|
16
20
|
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
|
17
21
|
|
|
18
22
|
export function App() {
|
|
@@ -34,6 +38,8 @@ export function App() {
|
|
|
34
38
|
client={rebaseClient}
|
|
35
39
|
authController={authController}
|
|
36
40
|
>
|
|
41
|
+
{/* The sign-in screen. On its own this changes nothing —
|
|
42
|
+
it is where you pass `loginView` to replace it. */}
|
|
37
43
|
<RebaseAuth />
|
|
38
44
|
<RebaseCMS
|
|
39
45
|
collections={collections}
|
|
@@ -35,6 +35,14 @@ export default defineConfig({
|
|
|
35
35
|
sourcemap: true,
|
|
36
36
|
rollupOptions: {
|
|
37
37
|
output: {
|
|
38
|
+
// These rules are copied in rather than taken from
|
|
39
|
+
// `rebaseManualChunks` in `@rebasepro/app/vitePlugin`, which is
|
|
40
|
+
// the same function and would let an upgrade improve them. The
|
|
41
|
+
// export does not exist in the `@rebasepro/app` version this
|
|
42
|
+
// scaffold pins, and a template may only import what the
|
|
43
|
+
// version it pins publishes — `pnpm check:templates` enforces
|
|
44
|
+
// that. Switch to the import in the first release that
|
|
45
|
+
// publishes it.
|
|
38
46
|
manualChunks(id) {
|
|
39
47
|
// Heavy vendor libraries — split into individually cached chunks.
|
|
40
48
|
//
|
|
@@ -26,11 +26,29 @@ drizzle/meta/
|
|
|
26
26
|
# Uploads
|
|
27
27
|
uploads/
|
|
28
28
|
|
|
29
|
+
# Database dumps (`rebase db backup`)
|
|
30
|
+
#
|
|
31
|
+
# The default destination is `backups/` under wherever the command was run, and
|
|
32
|
+
# `rebase db --help`'s own example (`--out ./backups`) lands in the tree too. A
|
|
33
|
+
# dump carries every row, including `rebase.users` password hashes, so this is
|
|
34
|
+
# not tidiness. `*.dump` covers the ones written somewhere else; the
|
|
35
|
+
# `.globals.sql` roles sidecar beside them carries no passwords.
|
|
36
|
+
backups/
|
|
37
|
+
*.dump
|
|
38
|
+
|
|
29
39
|
# Rebase dev
|
|
30
40
|
.rebase-dev-url
|
|
31
41
|
.rebase-dev-port
|
|
32
42
|
.rebase-dev-secrets.json
|
|
33
43
|
|
|
34
44
|
# Built project bundle (`rebase build`)
|
|
35
|
-
|
|
45
|
+
#
|
|
46
|
+
# The glob, not the one directory: a static app declared in `rebase.json` gets
|
|
47
|
+
# its own `dist-bundle-<name>/` beside this one, so the stock scaffold's first
|
|
48
|
+
# build left 307 untracked files behind `dist-bundle-admin/`.
|
|
49
|
+
dist-bundle*/
|
|
36
50
|
.rebase/
|
|
51
|
+
|
|
52
|
+
# Generated SDK (`rebase generate-sdk`) — regenerated from the collections,
|
|
53
|
+
# and stale copies in review diffs are noise nobody reads.
|
|
54
|
+
generated/
|
package/templates/template/npmrc
CHANGED
|
@@ -3,8 +3,17 @@
|
|
|
3
3
|
# This makes the project compatible with both pnpm and npm workspaces.
|
|
4
4
|
link-workspace-packages=true
|
|
5
5
|
|
|
6
|
-
#
|
|
7
|
-
|
|
6
|
+
# `verify-deps-before-run` and `confirm-modules-purge` used to be here too.
|
|
7
|
+
# They moved to pnpm-workspace.yaml, in their camelCase spelling: pnpm 11 no
|
|
8
|
+
# longer reads them from this file (it answers `undefined` for both), so they
|
|
9
|
+
# were doing nothing, while npm 12 warned about them on every install and every
|
|
10
|
+
# `npm run` — two lines of noise, in a first run, for settings that had already
|
|
11
|
+
# stopped applying.
|
|
8
12
|
|
|
9
|
-
#
|
|
10
|
-
|
|
13
|
+
# Refuse to install on a Node older than `engines.node` in package.json.
|
|
14
|
+
#
|
|
15
|
+
# npm-only: pnpm reads the same setting as `engineStrict` from
|
|
16
|
+
# pnpm-workspace.yaml, and answers `undefined` for this spelling. Kept here
|
|
17
|
+
# because a scaffold may be installed with either manager, and npm's default is
|
|
18
|
+
# a warning it then ignores — EBADENGINE, exit 0.
|
|
19
|
+
engine-strict=true
|
|
@@ -20,15 +20,21 @@
|
|
|
20
20
|
"schema:generate": "rebase schema generate --collections ../config/collections",
|
|
21
21
|
"generate:sdk": "rebase generate-sdk",
|
|
22
22
|
"skills:install": "rebase skills install",
|
|
23
|
+
"example": "tsx scripts/example.ts",
|
|
23
24
|
"deploy": "rebase cloud deploy"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@rebasepro/cli": "workspace:*",
|
|
27
28
|
"@rebasepro/types": "workspace:*",
|
|
29
|
+
"tsx": "^4.20.6",
|
|
28
30
|
"typescript": "^5.9.2"
|
|
29
31
|
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@rebasepro/client": "workspace:*",
|
|
34
|
+
"dotenv": "^17.4.2"
|
|
35
|
+
},
|
|
30
36
|
"engines": {
|
|
31
|
-
"node": ">=
|
|
37
|
+
"node": ">=22.22.0"
|
|
32
38
|
},
|
|
33
39
|
"pnpm": {
|
|
34
40
|
"onlyBuiltDependencies": [
|
|
@@ -36,5 +42,8 @@
|
|
|
36
42
|
"sharp",
|
|
37
43
|
"@ariga/atlas"
|
|
38
44
|
]
|
|
45
|
+
},
|
|
46
|
+
"allowScripts": {
|
|
47
|
+
"@ariga/atlas": true
|
|
39
48
|
}
|
|
40
49
|
}
|