@rebasepro/cli 0.17.3 → 0.18.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/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
package/dist/utils/project.d.ts
CHANGED
|
@@ -78,6 +78,72 @@ export declare function resolveTsx(projectRoot: string): string | null;
|
|
|
78
78
|
* error description string when it appears corrupted.
|
|
79
79
|
*/
|
|
80
80
|
export declare function validateTsxInstallation(tsxBinPath: string): string | null;
|
|
81
|
+
/**
|
|
82
|
+
* The one sentence a command says when the project's dependencies are missing.
|
|
83
|
+
*
|
|
84
|
+
* Six commands each had their own wording for the same state, and none of them
|
|
85
|
+
* named the remedy:
|
|
86
|
+
*
|
|
87
|
+
* ✗ Could not find CLI entry point for @rebasepro/server-postgres.
|
|
88
|
+
* ✗ Could not find tsx binary.
|
|
89
|
+
* ✗ Could not find tsx binary for backend.
|
|
90
|
+
*
|
|
91
|
+
* All three mean "you have not installed yet" — `getActiveBackendPlugin` has
|
|
92
|
+
* already read the driver out of `backend/package.json` by the time the first
|
|
93
|
+
* one fires, so the package is declared and simply not on disk, and `tsx` is a
|
|
94
|
+
* devDependency of every scaffold. But they read as *Rebase* being broken, and
|
|
95
|
+
* they name an internal path or a binary the developer never asked for rather
|
|
96
|
+
* than the command that fixes it. That is the whole of the failure someone sees
|
|
97
|
+
* on a fresh clone, where `node_modules/` is the one thing a checkout does not
|
|
98
|
+
* carry.
|
|
99
|
+
*
|
|
100
|
+
* So: one sentence, naming the package manager this project uses and the
|
|
101
|
+
* directory to run it in — the working directory is usually neither.
|
|
102
|
+
*/
|
|
103
|
+
export declare function dependenciesNotInstalled(projectRoot: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* Print {@link dependenciesNotInstalled} and exit 1.
|
|
106
|
+
*
|
|
107
|
+
* For the five commands that report and stop. `db.ts` throws instead, because
|
|
108
|
+
* its caller adds the `✗` and the exit itself.
|
|
109
|
+
*/
|
|
110
|
+
export declare function exitDependenciesNotInstalled(projectRoot: string): never;
|
|
111
|
+
/** One problem inside a refusal, when the command found several. */
|
|
112
|
+
export interface JsonFailureIssue {
|
|
113
|
+
/** Where it is — a config path, a manifest key, a file. */
|
|
114
|
+
path?: string;
|
|
115
|
+
message: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The refusal envelope, for a caller that asked for JSON.
|
|
119
|
+
*
|
|
120
|
+
* Same shape as the cloud family's `fail()` — `{ error: { message, code, hint } }`
|
|
121
|
+
* — because there is one CLI and a caller should not have to know which half of
|
|
122
|
+
* it answered. `code` is what a caller branches on and is never absent; the
|
|
123
|
+
* cloud family's reasoning about that applies here unchanged. `issues` carries
|
|
124
|
+
* the per-problem detail a human reader gets as a bullet list, because a
|
|
125
|
+
* refusal that says "2 problem(s)" and does not say which two is not
|
|
126
|
+
* machine-readable in any useful sense.
|
|
127
|
+
*
|
|
128
|
+
* On **stdout**, like every other `--json` result: the contract those commands
|
|
129
|
+
* make is that stdout holds one JSON value, and a caller that pipes stdout to a
|
|
130
|
+
* parser must get a parseable refusal rather than an empty stream and a
|
|
131
|
+
* human sentence it never sees. That contract used to hold for exactly one
|
|
132
|
+
* failure of each command — the one that goes through `requireProjectRoot` —
|
|
133
|
+
* while every other exit of the *same* command wrote human text to stderr and
|
|
134
|
+
* left stdout empty. So `rebase status --json` was parseable outside a project
|
|
135
|
+
* and unparseable inside a broken one, which is the case a caller actually has
|
|
136
|
+
* to handle.
|
|
137
|
+
*
|
|
138
|
+
* The flag is read off `process.argv` because the failure happens inside a
|
|
139
|
+
* helper the command calls before it has parsed anything. Coarse — a literal
|
|
140
|
+
* `--json` as some other flag's value would count — and worth it: the failure
|
|
141
|
+
* mode of being coarse is a JSON error where a human one was wanted, and the
|
|
142
|
+
* failure mode of not doing it is an unparseable stream.
|
|
143
|
+
*/
|
|
144
|
+
export declare function failAsJson(message: string, code: string, hint?: string, issues?: JsonFailureIssue[]): never;
|
|
145
|
+
/** Did the command line ask for machine-readable output? */
|
|
146
|
+
export declare function wantsJsonOutput(argv?: readonly string[]): boolean;
|
|
81
147
|
/**
|
|
82
148
|
* Require the project root or exit with a helpful error.
|
|
83
149
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function reportSpawnFailure(error: unknown): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one command the user probably meant, or nothing.
|
|
3
|
+
*
|
|
4
|
+
* Tight on purpose. A suggestion that is wrong is worse than none: it sends
|
|
5
|
+
* someone to read the help for a command that was never the answer, and on a
|
|
6
|
+
* family like `db` — where `push` and `pull` are one edit apart — a loose
|
|
7
|
+
* threshold would confidently propose the destructive neighbour of what they
|
|
8
|
+
* typed. So: one edit for a short word, two for a long one, and a prefix match
|
|
9
|
+
* always counts (`rebase cloud dep` for `deploy`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function suggestCommand(typed: string, known: readonly string[]): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The line itself, without printing it — so callers that must answer in JSON
|
|
14
|
+
* (the cloud family, whose `fail()` writes a machine-readable envelope) use the
|
|
15
|
+
* same words as the ones that print.
|
|
16
|
+
*
|
|
17
|
+
* `family` is empty for the top level: `rebase --help`, not `rebase --help`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function unknownCommandMessage(typed: string | undefined, known: readonly string[], family?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Print the line to stderr and exit 1.
|
|
22
|
+
*
|
|
23
|
+
* stderr, not stdout: a mistyped command is a failure, and the families with a
|
|
24
|
+
* `--json` mode guarantee that stdout holds only their envelope.
|
|
25
|
+
*/
|
|
26
|
+
export declare function unknownCommand(typed: string | undefined, known: readonly string[], family?: string): never;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The CLI's version, or `"unknown"` when it cannot be read. Memoized. */
|
|
2
|
+
export declare function cliVersion(): string;
|
|
3
|
+
/**
|
|
4
|
+
* `rebase-cli/0.17.3` — sent on every control-plane request.
|
|
5
|
+
*
|
|
6
|
+
* The control plane needs it to answer `CLI_TOO_OLD` with a minimum version
|
|
7
|
+
* instead of failing somewhere further in with a shape error. A client that
|
|
8
|
+
* does not identify itself can only ever be refused generically.
|
|
9
|
+
*/
|
|
10
|
+
export declare function cliUserAgent(): string;
|
package/package.json
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Developer tools for Rebase projects",
|
|
5
|
-
"main": "./dist/index.es.js",
|
|
6
|
-
"module": "./dist/index.es.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"type": "module",
|
|
9
|
-
"source": "src/index.ts",
|
|
10
|
-
"bin": {
|
|
11
|
-
"rebase": "bin/rebase.js"
|
|
12
|
-
},
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
5
|
"keywords": [
|
|
17
6
|
"cli",
|
|
18
7
|
"create-rebase-app",
|
|
@@ -22,8 +11,30 @@
|
|
|
22
11
|
"typescript",
|
|
23
12
|
"postgresql"
|
|
24
13
|
],
|
|
25
|
-
"
|
|
14
|
+
"homepage": "https://rebase.pro",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/rebasepro/rebase/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/rebasepro/rebase.git",
|
|
21
|
+
"directory": "packages/cli"
|
|
22
|
+
},
|
|
26
23
|
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22.22.0"
|
|
26
|
+
},
|
|
27
|
+
"author": "rebase.pro",
|
|
28
|
+
"main": "./dist/index.es.js",
|
|
29
|
+
"module": "./dist/index.es.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"bin": {
|
|
33
|
+
"rebase": "bin/rebase.js"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
27
38
|
"dependencies": {
|
|
28
39
|
"arg": "^5.0.2",
|
|
29
40
|
"chalk": "^5.6.2",
|
|
@@ -32,12 +43,12 @@
|
|
|
32
43
|
"inquirer": "14.0.2",
|
|
33
44
|
"jiti": "^2.7.0",
|
|
34
45
|
"pg": "^8.22.0",
|
|
35
|
-
"@rebasepro/agent-skills": "0.
|
|
36
|
-
"@rebasepro/codegen": "0.
|
|
37
|
-
"@rebasepro/
|
|
38
|
-
"@rebasepro/
|
|
39
|
-
"@rebasepro/
|
|
40
|
-
"@rebasepro/server
|
|
46
|
+
"@rebasepro/agent-skills": "0.18.1",
|
|
47
|
+
"@rebasepro/codegen": "0.18.1",
|
|
48
|
+
"@rebasepro/client": "0.18.1",
|
|
49
|
+
"@rebasepro/server-postgres": "0.18.1",
|
|
50
|
+
"@rebasepro/types": "0.18.1",
|
|
51
|
+
"@rebasepro/server": "0.18.1"
|
|
41
52
|
},
|
|
42
53
|
"devDependencies": {
|
|
43
54
|
"@types/node": "^26.1.2",
|
|
@@ -55,21 +66,18 @@
|
|
|
55
66
|
"exports": {
|
|
56
67
|
".": {
|
|
57
68
|
"types": "./dist/index.d.ts",
|
|
58
|
-
"import": "./dist/index.es.js"
|
|
69
|
+
"import": "./dist/index.es.js",
|
|
70
|
+
"default": "./dist/index.es.js"
|
|
59
71
|
}
|
|
60
72
|
},
|
|
61
|
-
"repository": {
|
|
62
|
-
"type": "git",
|
|
63
|
-
"url": "https://github.com/rebasepro/rebase.git",
|
|
64
|
-
"directory": "packages/cli"
|
|
65
|
-
},
|
|
66
73
|
"optionalDependencies": {
|
|
67
|
-
"@electric-sql/pglite": "
|
|
68
|
-
"@electric-sql/pglite-pgvector": "
|
|
69
|
-
"@electric-sql/pglite-socket": "
|
|
74
|
+
"@electric-sql/pglite": "0.5.6",
|
|
75
|
+
"@electric-sql/pglite-pgvector": "0.0.7",
|
|
76
|
+
"@electric-sql/pglite-socket": "0.2.9"
|
|
70
77
|
},
|
|
71
78
|
"scripts": {
|
|
72
79
|
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest",
|
|
73
81
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
74
82
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
75
83
|
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json && node ../../tooling/scripts/add-dts-extensions.mjs dist && node ../../tooling/scripts/assert-build-output.mjs",
|
|
@@ -3,6 +3,15 @@ import fs from "fs";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { loadEnv } from "@rebasepro/server";
|
|
6
|
+
// This should be `import { loadEnv, z } from "@rebasepro/server"`, and will be
|
|
7
|
+
// in the first release that publishes that `z`. `loadEnv({ extend })` composes
|
|
8
|
+
// the schema below with the framework's, and a schema built against a second
|
|
9
|
+
// copy of zod fails the framework's instanceof checks and is silently dropped —
|
|
10
|
+
// so taking `z` from the runtime is the only form that is reliably correct.
|
|
11
|
+
// The `@rebasepro/server` version an ejected project pins does not export it
|
|
12
|
+
// yet, and a template may only import what the version it pins publishes
|
|
13
|
+
// (`pnpm check:templates`); an import of a symbol that is not there fails at
|
|
14
|
+
// boot rather than at build. Switch both lines back when the version bumps.
|
|
6
15
|
import { z } from "zod";
|
|
7
16
|
|
|
8
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -15,11 +15,12 @@ import {
|
|
|
15
15
|
HonoEnv,
|
|
16
16
|
listenWithPortRetry,
|
|
17
17
|
cleanupDevPortFile,
|
|
18
|
+
initializeDataSources,
|
|
19
|
+
resolveDataSources,
|
|
18
20
|
resolveStorageSources,
|
|
19
21
|
logger
|
|
20
22
|
} from "@rebasepro/server";
|
|
21
|
-
import {
|
|
22
|
-
import { declaredStorageSources } from "@rebasepro/types";
|
|
23
|
+
import { declaredDataSources, declaredStorageSources } from "@rebasepro/types";
|
|
23
24
|
// Side-effect import: declaring is what registers, so anything that dropped
|
|
24
25
|
// this as "unused" would leave the backend with no buckets — silently.
|
|
25
26
|
import "../../config/resources.js";
|
|
@@ -35,10 +36,11 @@ import usersCollection from "../../config/collections/users.js";
|
|
|
35
36
|
const __filename = fileURLToPath(import.meta.url);
|
|
36
37
|
const __dirname = path.dirname(__filename);
|
|
37
38
|
|
|
38
|
-
// Which buckets this project has, read from the declarations in
|
|
39
|
+
// Which databases and buckets this project has, read from the declarations in
|
|
39
40
|
// `config/resources.ts`. One declaration site, so this process, the platform
|
|
40
41
|
// and the frontend all read the same list. A project that declares none gets
|
|
41
|
-
// one default source from the plain
|
|
42
|
+
// one default source of each, from the plain unsuffixed variables.
|
|
43
|
+
const dataSources = declaredDataSources();
|
|
42
44
|
const storageSources = declaredStorageSources();
|
|
43
45
|
|
|
44
46
|
// ─── App ─────────────────────────────────────────────────────────────
|
|
@@ -84,10 +86,21 @@ app.use("/*", cors({
|
|
|
84
86
|
|
|
85
87
|
app.use("/*", secureHeaders());
|
|
86
88
|
|
|
87
|
-
// ───
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// ─── Databases ───────────────────────────────────────────────────────
|
|
90
|
+
// One connection per declared database, resolved from `DATABASE_URL` for the
|
|
91
|
+
// default one and `DATABASE_URL__<KEY>` for every other — the same resolver the
|
|
92
|
+
// managed runtime uses, so this entrypoint cannot drift from it.
|
|
93
|
+
//
|
|
94
|
+
// It reads `process.env` rather than the typed `env` above on purpose: the
|
|
95
|
+
// suffixed names belong to sources this project declared, so no fixed schema
|
|
96
|
+
// can list them.
|
|
97
|
+
//
|
|
98
|
+
// Doing this by hand — one `createPostgresDatabaseConnection(env.DATABASE_URL)`
|
|
99
|
+
// — is what this file used to do, and it quietly broke the moment anyone
|
|
100
|
+
// declared a second database: collections routed to it fell back to the default
|
|
101
|
+
// driver and their rows landed in the wrong database, behind a server that
|
|
102
|
+
// looked healthy.
|
|
103
|
+
const resolvedDataSources = resolveDataSources(process.env, dataSources);
|
|
91
104
|
|
|
92
105
|
// ─── Start ───────────────────────────────────────────────────────────
|
|
93
106
|
async function startServer() {
|
|
@@ -102,6 +115,21 @@ async function startServer() {
|
|
|
102
115
|
// the same path is right from source and from `backend/dist/backend/src`.
|
|
103
116
|
const cronsDir = path.resolve(__dirname, "../crons");
|
|
104
117
|
|
|
118
|
+
// Open a connection per declared database. Sequential, and a failure on the
|
|
119
|
+
// second closes whatever the first opened rather than leaking it against a
|
|
120
|
+
// server this process is about to abandon.
|
|
121
|
+
const drivers = await initializeDataSources(
|
|
122
|
+
resolvedDataSources,
|
|
123
|
+
// {{#collections}}
|
|
124
|
+
{ tables, enums, relations },
|
|
125
|
+
// {{/collections}}
|
|
126
|
+
// {{^collections}}
|
|
127
|
+
// No generated schema: this project declares no collections in code.
|
|
128
|
+
undefined,
|
|
129
|
+
// {{/collections}}
|
|
130
|
+
[__dirname]
|
|
131
|
+
);
|
|
132
|
+
|
|
105
133
|
const backend = await initializeRebaseBackend({
|
|
106
134
|
// {{#collections}}
|
|
107
135
|
collectionsDir: path.resolve(__dirname, "../../config/collections"),
|
|
@@ -115,16 +143,11 @@ async function startServer() {
|
|
|
115
143
|
cronsDir: fs.existsSync(cronsDir) ? cronsDir : undefined,
|
|
116
144
|
server,
|
|
117
145
|
app,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
relations },
|
|
124
|
-
// {{/collections}}
|
|
125
|
-
adminConnectionString: env.ADMIN_CONNECTION_STRING || databaseUrl,
|
|
126
|
-
connectionString
|
|
127
|
-
}),
|
|
146
|
+
// One bootstrapper per data source, each registered under its own key —
|
|
147
|
+
// the key is exactly what `collection.dataSource` routes against. Only
|
|
148
|
+
// the default source is handed the generated schema, which describes
|
|
149
|
+
// this project's tables and they live in the default database.
|
|
150
|
+
bootstrappers: drivers.map(driver => driver.bootstrapper),
|
|
128
151
|
auth: {
|
|
129
152
|
// {{#collections}}
|
|
130
153
|
collection: usersCollection,
|
|
@@ -275,7 +298,11 @@ pass: env.SMTP_PASS! }
|
|
|
275
298
|
// Drains HTTP, stops crons, tears down realtime, then closes the pool.
|
|
276
299
|
// Guards against double signals and force-exits if shutdown hangs.
|
|
277
300
|
installShutdownHandlers(backend, {
|
|
278
|
-
|
|
301
|
+
// Every pool, not just the first: a second database left open holds a
|
|
302
|
+
// connection against the server while this process is meant to be gone.
|
|
303
|
+
onCleanup: async () => {
|
|
304
|
+
await Promise.all(drivers.map(driver => driver.connection.pool?.end()));
|
|
305
|
+
}
|
|
279
306
|
});
|
|
280
307
|
}
|
|
281
308
|
|
|
@@ -16,7 +16,7 @@ export const main = database();
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* The default bucket, bound from the plain unsuffixed storage variables
|
|
19
|
-
* (`S3_BUCKET`, `GCS_BUCKET
|
|
19
|
+
* (`S3_BUCKET`, `GCS_BUCKET`).
|
|
20
20
|
*
|
|
21
21
|
* Name more of them to get more — `bucket("media")` reads `S3_BUCKET__MEDIA`,
|
|
22
22
|
* and so on for every variable that kind uses.
|
|
@@ -22,8 +22,12 @@ CREATE POLICY your_table_owner ON your_table
|
|
|
22
22
|
`rebase.uid()`, `rebase.roles()` and `rebase.jwt()` are provided by Rebase and read the
|
|
23
23
|
identity of the authenticated request.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
`
|
|
25
|
+
Serving one anyway is `baas: { unprotectedTables: "serve" }`, and it is passed
|
|
26
|
+
to `initializeRebaseBackend` — so it needs a project that owns its entrypoint.
|
|
27
|
+
This scaffold has none: the published runtime boots it. Run `rebase eject` if
|
|
28
|
+
you want that file, and read the flag for what it is — every authenticated
|
|
29
|
+
request runs as one database role, so a table with no policy is handed whole to
|
|
30
|
+
every signed-in caller.
|
|
27
31
|
|
|
28
32
|
## Run it
|
|
29
33
|
|
|
@@ -32,24 +36,40 @@ pnpm install
|
|
|
32
36
|
pnpm dev
|
|
33
37
|
```
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
`rebase dev` prints the URL it bound, and a box with the API, the Swagger path
|
|
40
|
+
and the database it is using. **Read that box**: the port is derived from this
|
|
41
|
+
project's path rather than fixed, so several Rebase projects can run at once,
|
|
42
|
+
and `PORT` in `.env` applies to `rebase start`, not here.
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
- API: `<the printed URL>/api/data/<table>` — once a table is served
|
|
45
|
+
- Docs: `<the printed URL>/api/swagger` — likewise
|
|
46
|
+
- Health: `<the printed URL>/health`
|
|
47
|
+
|
|
48
|
+
A headless project starts with no tables, so the first two answer
|
|
49
|
+
`404 NO_COLLECTIONS` until you create one and restart — the box `rebase dev`
|
|
50
|
+
prints says the same thing. See [Serving a table](#serving-a-table).
|
|
51
|
+
|
|
52
|
+
There is no database to install: with no `DATABASE_URL` set, `rebase dev` runs a
|
|
53
|
+
managed PostgreSQL for this project, with its data under `.rebase/`. Set
|
|
54
|
+
`DATABASE_URL` in `.env` to point at a database of your own — it always wins.
|
|
55
|
+
`rebase db url` prints whichever one is in use, so it pipes straight into psql.
|
|
40
56
|
|
|
41
57
|
## Use it from an app
|
|
42
58
|
|
|
43
59
|
```ts
|
|
44
60
|
import { createRebaseClient } from "@rebasepro/client";
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
// The URL `rebase dev` printed. `pnpm example` reads it from .rebase-dev-url
|
|
63
|
+
// for you rather than hardcoding a port.
|
|
64
|
+
const rebase = createRebaseClient({ baseUrl: process.env.REBASE_URL! });
|
|
47
65
|
const posts = await rebase.data.collection("posts").find();
|
|
48
66
|
```
|
|
49
67
|
|
|
50
68
|
## Adding an admin UI later
|
|
51
69
|
|
|
52
|
-
Nothing here locks you out of it.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
Nothing here locks you out of it. Add a `config/collections/` directory and a
|
|
71
|
+
frontend that renders them: the backend serves an admin panel once collections
|
|
72
|
+
exist, because the mode is derived from whether any are declared rather than
|
|
73
|
+
set anywhere. There is no `backend/src/index.ts` to edit — the published
|
|
74
|
+
runtime boots this project — and `rebase eject` writes one if you want to own
|
|
75
|
+
the entrypoint.
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
"@hono/node-server": "^2.0.12",
|
|
16
16
|
"pg": "^8.11.3",
|
|
17
17
|
"ws": "^8.16.0",
|
|
18
|
-
"dotenv": "^
|
|
18
|
+
"dotenv": "^17.4.2",
|
|
19
19
|
"zod": "^4.4.3"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/pg": "^8.6.5",
|
|
23
|
-
"@types/node": "^
|
|
23
|
+
"@types/node": "^22.19.4",
|
|
24
24
|
"@types/ws": "^8.5.10",
|
|
25
25
|
"tsx": "^4.20.6",
|
|
26
26
|
"typescript": "^5.9.2"
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@rebasepro/client": "workspace:*",
|
|
30
|
-
"dotenv": "^
|
|
30
|
+
"dotenv": "^17.4.2"
|
|
31
31
|
},
|
|
32
32
|
"engines": {
|
|
33
|
-
"node": ">=
|
|
33
|
+
"node": ">=22.22.0"
|
|
34
34
|
},
|
|
35
35
|
"pnpm": {
|
|
36
36
|
"onlyBuiltDependencies": [
|
|
@@ -38,5 +38,8 @@
|
|
|
38
38
|
"sharp",
|
|
39
39
|
"@ariga/atlas"
|
|
40
40
|
]
|
|
41
|
+
},
|
|
42
|
+
"allowScripts": {
|
|
43
|
+
"@ariga/atlas": true
|
|
41
44
|
}
|
|
42
45
|
}
|
|
@@ -4,6 +4,19 @@ packages:
|
|
|
4
4
|
linkWorkspacePackages: true
|
|
5
5
|
blockExoticSubdeps: false
|
|
6
6
|
minimumReleaseAge: 0
|
|
7
|
+
# See the note in templates/template/pnpm-workspace.yaml: pnpm 11 reads these
|
|
8
|
+
# here and no longer from `.npmrc`, where npm 12 also warns about them.
|
|
9
|
+
verifyDepsBeforeRun: false
|
|
10
|
+
# Refuse to install on a Node older than `engines.node` in package.json.
|
|
11
|
+
#
|
|
12
|
+
# Off by default, in every package manager, which is why the floor this
|
|
13
|
+
# scaffold declares was enforced by nothing: pnpm 11 installs a project whose
|
|
14
|
+
# `engines.node` is `>=99.0.0` and exits 0, and npm prints EBADENGINE and also
|
|
15
|
+
# exits 0. So a first run on Node 20 said nothing at all, and the first sign was
|
|
16
|
+
# a syntax error from inside a dependency. `engine-strict=true` in npmrc is the
|
|
17
|
+
# same setting for npm.
|
|
18
|
+
engineStrict: true
|
|
19
|
+
confirmModulesPurge: false
|
|
7
20
|
allowBuilds:
|
|
8
21
|
esbuild: true
|
|
9
22
|
sharp: true
|
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
# ╚══════════════════════════════════════════════════════════════════════════════╝
|
|
5
5
|
|
|
6
6
|
# ── Database ──────────────────────────────────────────────────────────────────
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# DATABASE_URL: unset means the managed database; set means yours.
|
|
8
|
+
#
|
|
9
|
+
# Leave it unset and `rebase dev` starts a managed PostgreSQL for this project,
|
|
10
|
+
# with its data under .rebase/ — no Docker, nothing to install. Set it and that
|
|
11
|
+
# database wins over the managed one, always: PostgreSQL (postgresql://) or
|
|
12
|
+
# MongoDB (mongodb:// or mongodb+srv://), detected from the scheme.
|
|
13
|
+
#
|
|
14
|
+
# `rebase init` writes your project's `.env` with this line commented out, which
|
|
15
|
+
# is what puts a new project on the managed database. `rebase db url` prints
|
|
16
|
+
# whichever one is in use.
|
|
17
|
+
#
|
|
10
18
|
# sslmode=disable matches the local docker-compose database, which has no TLS;
|
|
11
19
|
# schema tooling (atlas) would otherwise default to requiring SSL. Remove it
|
|
12
20
|
# when pointing at a managed/cloud database.
|
|
@@ -24,6 +32,15 @@ DATABASE_URL=postgresql://rebase_app:changeme@localhost:5432/rebase?options=-c%2
|
|
|
24
32
|
# explicitly for a local database, say so:
|
|
25
33
|
# DATABASE_URL=postgresql://rebase_app:changeme@localhost:5432/rebase?options=-c%20search_path%3Dpublic&sslmode=disable
|
|
26
34
|
|
|
35
|
+
# The password for the compose database, and the one inside the DATABASE_URL
|
|
36
|
+
# above. `rebase init` generates it and writes both into `.env`, so they cannot
|
|
37
|
+
# disagree; docker-compose.yml falls back to `changeme` when it is unset.
|
|
38
|
+
#
|
|
39
|
+
# Commented out on purpose: `init` writes its own line above, and a second
|
|
40
|
+
# assignment here would win — dotenv and compose both take the last one, so an
|
|
41
|
+
# uncommented default would silently replace the generated password.
|
|
42
|
+
# DATABASE_PASSWORD=changeme
|
|
43
|
+
|
|
27
44
|
# Separate admin connection string for migrations and schema operations (optional)
|
|
28
45
|
# Falls back to DATABASE_URL if not set
|
|
29
46
|
# ADMIN_CONNECTION_STRING=postgresql://postgres:your-password@localhost:5432/rebase
|
|
@@ -53,9 +70,42 @@ JWT_SECRET=
|
|
|
53
70
|
JWT_ACCESS_EXPIRES_IN=1h
|
|
54
71
|
JWT_REFRESH_EXPIRES_IN=30d
|
|
55
72
|
|
|
73
|
+
# Does the data API demand a token before it looks at anything? Defaults to
|
|
74
|
+
# `true`. Set `false` for a backend a public website reads — row-level security
|
|
75
|
+
# still decides which rows come back, this only removes the gate in front of it,
|
|
76
|
+
# so a collection with no `select` rule still returns nothing.
|
|
77
|
+
# AUTH_REQUIRE=false
|
|
78
|
+
|
|
56
79
|
# ── Registration ──────────────────────────────────────────────────────────────
|
|
57
80
|
ALLOW_REGISTRATION=true
|
|
58
81
|
|
|
82
|
+
# The hard kill switch. `ALLOW_REGISTRATION=false` still admits the first
|
|
83
|
+
# account into an empty table — it has to, or a fresh database can never
|
|
84
|
+
# produce the signed-in caller that bootstrapping an admin needs. This closes
|
|
85
|
+
# that window too, so pair it with the first-admin variables below or the
|
|
86
|
+
# deployment has no way in at all. docker-compose.yml defaults it to `true`.
|
|
87
|
+
# DISABLE_SELF_REGISTRATION=true
|
|
88
|
+
|
|
89
|
+
# ── The first admin account ───────────────────────────────────────────────────
|
|
90
|
+
# Outside production the first account to register is promoted to admin, which
|
|
91
|
+
# is what makes `rebase dev` one command. In production that window is closed:
|
|
92
|
+
# a deployment is answering on a hostname before you have typed anything, so a
|
|
93
|
+
# first-come-first-admin form is a race the operator can lose.
|
|
94
|
+
#
|
|
95
|
+
# So production names its first admin instead. The runtime creates the account
|
|
96
|
+
# once, while the user table is empty, and does nothing on every boot after
|
|
97
|
+
# that. docker-compose.yml requires both variables, and `rebase init` wrote
|
|
98
|
+
# them here with a generated password.
|
|
99
|
+
#
|
|
100
|
+
# `rebase dev` ignores both and says so at boot: locally the first account to
|
|
101
|
+
# register is still the way in, and seeding here would spend that window before
|
|
102
|
+
# you opened the app.
|
|
103
|
+
#
|
|
104
|
+
# Change the email to yours. Sign in, then change the password — this file is
|
|
105
|
+
# plain text on disk.
|
|
106
|
+
REBASE_ADMIN_EMAIL=
|
|
107
|
+
REBASE_ADMIN_PASSWORD=
|
|
108
|
+
|
|
59
109
|
# ── Email (optional — required for password reset / verification) ─────────────
|
|
60
110
|
# SMTP_HOST=smtp.example.com
|
|
61
111
|
# SMTP_PORT=587
|
|
@@ -70,7 +120,14 @@ ALLOW_REGISTRATION=true
|
|
|
70
120
|
# EMAIL_LOGO_URL=https://yourapp.com/logo.png
|
|
71
121
|
|
|
72
122
|
# ── URLs ──────────────────────────────────────────────────────────────────────
|
|
73
|
-
# Canonical frontend URL
|
|
123
|
+
# Canonical frontend URL. It is the base of every emailed link (welcome,
|
|
124
|
+
# password reset, verification) and one of the two things CORS is derived from.
|
|
125
|
+
#
|
|
126
|
+
# `rebase dev` OVERRIDES this with the port it actually starts the frontend on,
|
|
127
|
+
# so you do not need to keep it in step during development — the value below is
|
|
128
|
+
# only a sensible default for the tools that read the file directly. Set it to
|
|
129
|
+
# your real site for a deployment; in production the server refuses to boot with
|
|
130
|
+
# neither this nor CORS_ORIGINS.
|
|
74
131
|
FRONTEND_URL=http://localhost:5173
|
|
75
132
|
|
|
76
133
|
# Allowed CORS origins in production (comma-separated)
|
|
@@ -138,11 +195,15 @@ VITE_API_URL=
|
|
|
138
195
|
# interface and pass it directly in your backend config.
|
|
139
196
|
|
|
140
197
|
# --- Several buckets ------------------------------------------------------
|
|
141
|
-
# Declare them in
|
|
142
|
-
# carrying its own suffix. The default source takes no suffix, so
|
|
143
|
-
# above keeps working and a single-bucket project declares nothing.
|
|
198
|
+
# Declare them in config/resources.ts, then configure each from the SAME
|
|
199
|
+
# variable names carrying its own suffix. The default source takes no suffix, so
|
|
200
|
+
# everything above keeps working and a single-bucket project declares nothing.
|
|
144
201
|
#
|
|
145
|
-
#
|
|
202
|
+
# config/resources.ts: export const media = bucket("media", { engine: "s3" });
|
|
203
|
+
#
|
|
204
|
+
# Then `rebase resources --write` regenerates rebase.resources.json, which is
|
|
205
|
+
# what readers that cannot run your code — the build, the control plane — see.
|
|
206
|
+
# `rebase status` prints each bucket with the variables it is actually reading.
|
|
146
207
|
#
|
|
147
208
|
# STORAGE_TYPE__MEDIA=s3
|
|
148
209
|
# S3_BUCKET__MEDIA=my-app-media
|
|
@@ -173,3 +234,25 @@ VITE_API_URL=
|
|
|
173
234
|
# Falls back to false. Useful for databases with application-only roles or managed setups.
|
|
174
235
|
# DISABLE_DB_ROLE_SWITCHING=true
|
|
175
236
|
|
|
237
|
+
# ── Collection config strictness (optional) ───────────────────────────────────
|
|
238
|
+
# What boot does with a key in your collections that this version does not read.
|
|
239
|
+
# A key it knows *moved* is always fatal; this governs the ones it simply does
|
|
240
|
+
# not recognise, which are usually a typo and occasionally deliberate metadata.
|
|
241
|
+
# warn log it and boot (the default)
|
|
242
|
+
# error refuse to boot — worth turning on in CI
|
|
243
|
+
# off say nothing
|
|
244
|
+
# REBASE_STRICT_COLLECTION_CONFIG=error
|
|
245
|
+
|
|
246
|
+
# ── Self-hosting (docker compose) ─────────────────────────────────────────────
|
|
247
|
+
# The runtime image tag the compose file runs: `rebasepro/server:${REBASE_VERSION}`.
|
|
248
|
+
# `rebase init` pins it to the version it scaffolded against, so a project does
|
|
249
|
+
# not silently move to a new runtime; `latest` is only the fallback when it is
|
|
250
|
+
# unset. To upgrade, change this and restart — your bundle is untouched.
|
|
251
|
+
REBASE_VERSION=latest
|
|
252
|
+
|
|
253
|
+
# What boot does about versioned migrations in `backend/migrations`.
|
|
254
|
+
# ensure create missing tables and columns from your collections (the default)
|
|
255
|
+
# migrate run the migration files, then ensure
|
|
256
|
+
# off neither
|
|
257
|
+
# REBASE_MIGRATE_ON_BOOT=ensure
|
|
258
|
+
|