@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.
Files changed (103) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +21 -3
  3. package/bin/rebase.js +93 -6
  4. package/dist/branch-pointer-dBiClJ0p.js +111 -0
  5. package/dist/branch-pointer-dBiClJ0p.js.map +1 -0
  6. package/dist/bundle.d.ts +80 -3
  7. package/dist/cli.d.ts +22 -0
  8. package/dist/commands/auth.d.ts +13 -0
  9. package/dist/commands/cloud/action-help.d.ts +0 -10
  10. package/dist/commands/cloud/auth.d.ts +37 -0
  11. package/dist/commands/cloud/context.d.ts +90 -2
  12. package/dist/commands/cloud/databases.d.ts +0 -2
  13. package/dist/commands/cloud/declared-resources.d.ts +3 -0
  14. package/dist/commands/cloud/deploy.d.ts +76 -6
  15. package/dist/commands/cloud/deployments.d.ts +9 -2
  16. package/dist/commands/cloud/index.d.ts +36 -0
  17. package/dist/commands/cloud/link.d.ts +1 -1
  18. package/dist/commands/cloud/orgs.d.ts +6 -0
  19. package/dist/commands/cloud/resources.d.ts +81 -3
  20. package/dist/commands/cloud/settings.d.ts +7 -0
  21. package/dist/commands/db.d.ts +119 -9
  22. package/dist/commands/dev.d.ts +216 -1
  23. package/dist/commands/doctor.d.ts +51 -0
  24. package/dist/commands/init.d.ts +1 -0
  25. package/dist/commands/skills.d.ts +82 -13
  26. package/dist/commands/status.d.ts +1 -0
  27. package/dist/commands/telemetry.d.ts +0 -8
  28. package/dist/{daemon-Bdl4lrdt.js → daemon-YDZK9NZ1.js} +88 -9
  29. package/dist/daemon-YDZK9NZ1.js.map +1 -0
  30. package/dist/{daemon-entry-CmJn83zu.js → daemon-entry-LTFKtpHy.js} +117 -44
  31. package/dist/daemon-entry-LTFKtpHy.js.map +1 -0
  32. package/dist/dev-db/branch-pointer.d.ts +71 -0
  33. package/dist/dev-db/daemon.d.ts +27 -0
  34. package/dist/dev-db/prepare.d.ts +62 -0
  35. package/dist/dev-db/pull.d.ts +28 -0
  36. package/dist/dev-db/resolve.d.ts +45 -6
  37. package/dist/dev-db/state.d.ts +21 -0
  38. package/dist/dev-preflight-CNLl4rdo.js +367 -0
  39. package/dist/dev-preflight-CNLl4rdo.js.map +1 -0
  40. package/dist/doctor-environment.d.ts +131 -0
  41. package/dist/function-portability.d.ts +7 -0
  42. package/dist/index.d.ts +25 -15
  43. package/dist/index.es.js +5594 -1741
  44. package/dist/index.es.js.map +1 -1
  45. package/dist/project-3WCbrrfW.js +499 -0
  46. package/dist/project-3WCbrrfW.js.map +1 -0
  47. package/dist/{pull-DqPRu1te.js → pull-Bj0XnH5s.js} +31 -2
  48. package/dist/pull-Bj0XnH5s.js.map +1 -0
  49. package/dist/resolve-Y56osuQH.js +74 -0
  50. package/dist/resolve-Y56osuQH.js.map +1 -0
  51. package/dist/resources/derive.d.ts +67 -1
  52. package/dist/resources/status.d.ts +128 -0
  53. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  54. package/dist/{state-c0CJ6Kwb.js → state-C59Elrnt.js} +24 -15
  55. package/dist/state-C59Elrnt.js.map +1 -0
  56. package/dist/utils/args.d.ts +18 -0
  57. package/dist/utils/command-words.d.ts +45 -0
  58. package/dist/utils/dev-preflight.d.ts +46 -0
  59. package/dist/utils/package-manager.d.ts +23 -0
  60. package/dist/utils/project.d.ts +66 -0
  61. package/dist/utils/spawn-error.d.ts +1 -0
  62. package/dist/utils/unknown-command.d.ts +26 -0
  63. package/dist/utils/version.d.ts +10 -0
  64. package/package.json +36 -28
  65. package/templates/eject/backend/src/env.ts +9 -0
  66. package/templates/eject/backend/src/index.ts +46 -19
  67. package/templates/eject/config/resources.ts +1 -1
  68. package/templates/overlays/baas/README.md +31 -11
  69. package/templates/overlays/baas/backend/package.json +2 -2
  70. package/templates/overlays/baas/backend/tsconfig.json +1 -1
  71. package/templates/overlays/baas/package.json +5 -2
  72. package/templates/overlays/baas/pnpm-workspace.yaml +13 -0
  73. package/templates/template/.cursorrules +1 -0
  74. package/templates/template/.env.example +91 -8
  75. package/templates/template/.github/copilot-instructions.md +1 -0
  76. package/templates/template/.mcp.json +11 -0
  77. package/templates/template/.windsurfrules +1 -0
  78. package/templates/template/AGENTS.md +1 -0
  79. package/templates/template/CLAUDE.md +2 -1
  80. package/templates/template/README.md +59 -28
  81. package/templates/template/ai-instructions.md +29 -12
  82. package/templates/template/backend/functions/hello.ts +5 -1
  83. package/templates/template/backend/package.json +2 -2
  84. package/templates/template/backend/src/schema.generated.ts +8 -3
  85. package/templates/template/backend/tsconfig.json +1 -1
  86. package/templates/template/config/cms.d.ts +6 -0
  87. package/templates/template/config/package.json +1 -1
  88. package/templates/template/config/resources.ts +42 -3
  89. package/templates/template/config/storage.ts +2 -1
  90. package/templates/template/docker-compose.yml +50 -9
  91. package/templates/template/frontend/index.html +0 -1
  92. package/templates/template/frontend/package.json +1 -2
  93. package/templates/template/frontend/src/App.tsx +8 -2
  94. package/templates/template/frontend/vite.config.ts +8 -0
  95. package/templates/template/gitignore +19 -1
  96. package/templates/template/npmrc +13 -4
  97. package/templates/template/package.json +10 -1
  98. package/templates/template/pnpm-workspace.yaml +17 -0
  99. package/templates/template/scripts/example.ts +4 -1
  100. package/dist/daemon-Bdl4lrdt.js.map +0 -1
  101. package/dist/daemon-entry-CmJn83zu.js.map +0 -1
  102. package/dist/pull-DqPRu1te.js.map +0 -1
  103. package/dist/state-c0CJ6Kwb.js.map +0 -1
@@ -1,2 +1,3 @@
1
1
  # Rebase AI Rules
2
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 windsurf`.
@@ -1,2 +1,3 @@
1
1
  # Rebase AI Rules
2
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 codex`.
@@ -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) >= 18
15
+ - [Node.js](https://nodejs.org) >= 22.22
16
16
  - [pnpm](https://pnpm.io) or [npm](https://www.npmjs.com) (v7+)
17
- - [Docker](https://www.docker.com) (to run the included PostgreSQL container),
18
- or your own PostgreSQL database
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 PostgreSQL database container:
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
- `rebase dev` prints the two URLs it actually bound and opens the admin panel
47
- there. The first account you register becomes the admin.
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
- > The `db:push` step is what creates the tables for the example `posts`,
56
- > `authors`, and `tags` collections. Skip it and the admin panel still opens,
57
- > but those collections will be empty and their API calls will fail until the
58
- > tables exist.
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.call("functions/hello", { name: "World" })`
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 via `dotenv` from `../../.env` (relative to `backend/src/`)
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
- The runtime creates its auth tables at boot but **not** your collection tables —
137
- a container restart must not be able to change a schema as a side effect — so
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 and create the tables from your collections
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 provides agent skills for AI coding assistants (Cursor, Claude Code, Windsurf, Gemini CLI, Antigravity, and more) with detailed guidelines, architecture, SDK APIs, and troubleshooting guides.
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
- To install skills for your environment, run:
5
+ ## Commands
6
6
 
7
- ```
8
- rebase skills install
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
- ## Core Tenets (Quick Reference)
21
+ ## Never
12
22
 
13
- 1. **Schema-as-Code**: Always define or edit collections in `config/collections/` (e.g., `config/collections/posts.ts`). Never modify generated Drizzle schemas or PostgreSQL tables manually.
14
- 2. **Two-Step Migrations**:
15
- - Step 1: Run `rebase schema generate` to compile collections to the Drizzle schema.
16
- - Step 2: Run `rebase db push` (development) or `rebase db generate && rebase db migrate` (production) to apply schema changes to the database.
17
- 3. **Use the SDK**: Always use the Rebase SDK (`rebase.data.<slug>`) to fetch or modify data. Bypassing it with raw SQL or direct Drizzle/PG queries circumvents model validations, lifecycle hooks, and Row-Level Security (RLS).
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). The `rebase-design-language` and `rebase-ui-components` skills cover this in full; install them with `rebase skills install`.
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.call("functions/hello", { name: "World" });
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": "^16.0.0",
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": "^20.10.5",
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 auto-generated by `pnpm db:generate`.
2
- // Do not edit manually.
1
+ // This file is generated from config/collections/. Do not edit manually.
3
2
  //
4
- // Run `pnpm db:generate` to regenerate this file from your collections.
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 = {};
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
4
  "module": "ESNext",
5
- "moduleResolution": "node",
5
+ "moduleResolution": "bundler",
6
6
  "lib": ["ES2022"],
7
7
  "outDir": "./dist",
8
8
  "strict": true,
@@ -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.
@@ -16,7 +16,7 @@
16
16
  "@rebasepro/types": "workspace:*"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/node": "^20.19.41",
19
+ "@types/node": "^22.19.4",
20
20
  "typescript": "^5.9.2"
21
21
  },
22
22
  "exports": {
@@ -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` / `STORAGE_BUCKET`; a named one appends its
39
- * key, so `bucket("media")` reads `S3_BUCKET__MEDIA`.
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` in `backend/src/index.ts`.
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
- # rebase db push # create the collection tables, once
16
- # docker compose up
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
- # Auth tables are created at boot. Collection tables are not: run
101
- # `rebase db push` once, against the database above. A container restart
102
- # must not be able to change a schema as a side effect.
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. Writable, because the runtime installs the bundle's
115
- # declared dependencies into it on first start `rebase build` emits a
116
- # package.json but not a node_modules.
117
- - ./dist-bundle:/bundle
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:
@@ -2,7 +2,6 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
6
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
6
  <title>{{PROJECT_NAME}}</title>
8
7
  </head>
@@ -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": "^20.19.41",
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
- // Configuration from environment
15
- const API_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? "http://localhost:3001" : undefined);
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
- dist-bundle/
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/
@@ -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
- # Disable automatic dependency check before running scripts to prevent background install failures in read-only environments
7
- verify-deps-before-run=false
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
- # Prevent pnpm from prompting for confirmation when purging modules in non-TTY environments (e.g. Docker, CI)
10
- confirm-modules-purge=false
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": ">=18.0.0"
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
  }