@rebasepro/cli 0.9.1-canary.fd3754b → 0.9.1-canary.ff338b5

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 (31) hide show
  1. package/bin/rebase.js +67 -1
  2. package/dist/commands/cloud/context.d.ts +99 -9
  3. package/dist/commands/cloud/deploy.d.ts +2 -2
  4. package/dist/commands/cloud/deployments.d.ts +38 -0
  5. package/dist/commands/cloud/domains.d.ts +1 -0
  6. package/dist/commands/cloud/env.d.ts +6 -0
  7. package/dist/commands/cloud/extensions.d.ts +3 -0
  8. package/dist/commands/cloud/power.d.ts +3 -0
  9. package/dist/commands/cloud/projects.d.ts +2 -2
  10. package/dist/commands/cloud/resources.d.ts +1 -1
  11. package/dist/commands/cloud/settings.d.ts +8 -0
  12. package/dist/commands/init.d.ts +8 -0
  13. package/dist/commands/skills.d.ts +1 -1
  14. package/dist/index.es.js +3459 -1594
  15. package/dist/index.es.js.map +1 -1
  16. package/dist/utils/package-manager.d.ts +19 -5
  17. package/package.json +8 -8
  18. package/templates/overlays/baas/README.md +19 -1
  19. package/templates/overlays/baas/backend/package.json +2 -2
  20. package/templates/overlays/baas/backend/src/index.ts +23 -3
  21. package/templates/overlays/baas/backend/tsconfig.json +2 -1
  22. package/templates/template/.env.example +13 -3
  23. package/templates/template/backend/package.json +4 -3
  24. package/templates/template/backend/src/env.ts +30 -1
  25. package/templates/template/backend/src/index.ts +18 -2
  26. package/templates/template/config/collections/presets/blank/index.ts +3 -1
  27. package/templates/template/config/collections/presets/ecommerce/index.ts +3 -1
  28. package/templates/template/config/collections/users.ts +2 -0
  29. package/templates/template/frontend/src/App.tsx +1 -2
  30. package/templates/template/gitignore +31 -0
  31. package/templates/template/npmrc +10 -0
@@ -20,13 +20,27 @@ export interface PMCommands {
20
20
  workspaceProtocol: string;
21
21
  }
22
22
  /**
23
- * Detect the package manager from the environment or the target directory.
23
+ * Whether pnpm is runnable on this machine.
24
+ *
25
+ * Used to decide whether a fresh project can be scaffolded with pnpm. Kept
26
+ * cheap and non-interactive (short timeout, output discarded) so it never
27
+ * hangs detection if a corepack shim misbehaves.
28
+ */
29
+ export declare function isPnpmAvailable(): boolean;
30
+ /**
31
+ * Detect the package manager for a Rebase project.
32
+ *
33
+ * Rebase recommends pnpm, so detection prefers it. Crucially, *how the CLI was
34
+ * invoked* (`npx` vs `pnpm dlx`, i.e. `npm_config_user_agent`) is deliberately
35
+ * ignored: running `npx @rebasepro/cli init` says nothing about how the user
36
+ * wants to manage the project they're creating, and letting it pin the scaffold
37
+ * to npm is what made every `npx`-invoked project an npm project.
24
38
  *
25
39
  * Detection order:
26
- * 1. Explicit override (if provided)
27
- * 2. `npm_config_user_agent` env var (set by npm/pnpm when running via `npx`/`pnpm dlx`)
28
- * 3. Lock-file presence in the target directory
29
- * 4. Default to pnpm (Rebase's recommended PM)
40
+ * 1. An existing lock file — an explicit choice we always respect
41
+ * (`pnpm-lock.yaml` wins over `package-lock.json` when both are present).
42
+ * 2. pnpm, whenever it is installed.
43
+ * 3. npm, only as a fallback when pnpm is genuinely unavailable.
30
44
  */
31
45
  export declare function detectPackageManager(targetDir?: string): PackageManager;
32
46
  /** Build the command helpers for a given package manager. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebasepro/cli",
3
- "version": "0.9.1-canary.fd3754b",
3
+ "version": "0.9.1-canary.ff338b5",
4
4
  "description": "Developer tools for Rebase projects",
5
5
  "main": "./dist/index.es.js",
6
6
  "module": "./dist/index.es.js",
@@ -31,12 +31,12 @@
31
31
  "execa": "^9.6.1",
32
32
  "inquirer": "14.0.2",
33
33
  "jiti": "^2.7.0",
34
- "@rebasepro/agent-skills": "0.9.1-canary.fd3754b",
35
- "@rebasepro/codegen": "0.9.1-canary.fd3754b",
36
- "@rebasepro/server": "0.9.1-canary.fd3754b",
37
- "@rebasepro/types": "0.9.1-canary.fd3754b",
38
- "@rebasepro/client": "0.9.1-canary.fd3754b",
39
- "@rebasepro/server-postgres": "0.9.1-canary.fd3754b"
34
+ "@rebasepro/agent-skills": "0.9.1-canary.ff338b5",
35
+ "@rebasepro/codegen": "0.9.1-canary.ff338b5",
36
+ "@rebasepro/server": "0.9.1-canary.ff338b5",
37
+ "@rebasepro/types": "0.9.1-canary.ff338b5",
38
+ "@rebasepro/server-postgres": "0.9.1-canary.ff338b5",
39
+ "@rebasepro/client": "0.9.1-canary.ff338b5"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^25.9.3",
@@ -65,7 +65,7 @@
65
65
  "scripts": {
66
66
  "test": "vitest run",
67
67
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
68
- "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json",
68
+ "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json && node ../../scripts/assert-build-output.mjs",
69
69
  "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
70
70
  }
71
71
  }
@@ -4,9 +4,27 @@ A headless Rebase backend: a REST API, auth, storage, realtime and backups over
4
4
  your PostgreSQL database.
5
5
 
6
6
  There are no collection files. The server reads your database schema at boot and
7
- serves every table, so the API follows your migrations — change the schema and
7
+ serves your tables, so the API follows your migrations — change the schema and
8
8
  the endpoints change with it.
9
9
 
10
+ ## Serving a table
11
+
12
+ A table is served once it has an authorization model: row-level security enabled
13
+ plus at least one policy. Until then the server skips it — deliberately, so a new
14
+ table is never exposed just by existing — and logs each table it skipped and why.
15
+
16
+ ```sql
17
+ ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
18
+ CREATE POLICY your_table_owner ON your_table
19
+ FOR ALL USING (user_id = auth.uid());
20
+ ```
21
+
22
+ `auth.uid()`, `auth.roles()` and `auth.jwt()` are provided by Rebase and read the
23
+ identity of the authenticated request.
24
+
25
+ To serve unprotected tables anyway (development only), set
26
+ `baas: { unprotectedTables: "serve" }` in `backend/src/index.ts`.
27
+
10
28
  ## Run it
11
29
 
12
30
  ```bash
@@ -5,9 +5,9 @@
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "dev": "tsx watch src/index.ts",
8
+ "dev": "tsx watch --include=\"./functions/**/*\" src/index.ts",
9
9
  "build": "tsc",
10
- "start": "node dist/backend/src/index.js"
10
+ "start": "node dist/index.js"
11
11
  },
12
12
  "dependencies": {
13
13
  "@rebasepro/server": "workspace:*",
@@ -36,10 +36,26 @@ const allowedOrigins = isProduction
36
36
  })()
37
37
  : [];
38
38
 
39
+ // In dev we still restrict which origins are reflected. Because `credentials`
40
+ // is enabled, reflecting an arbitrary Origin would let any website the
41
+ // developer happens to visit make credentialed cross-origin requests to this
42
+ // dev server (and read the responses) using the developer's session. So dev
43
+ // reflects only localhost origins; requests with no Origin (curl, same-origin)
44
+ // are unaffected.
45
+ const isLocalhostOrigin = (origin: string): boolean => {
46
+ try {
47
+ const { hostname } = new URL(origin);
48
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
49
+ } catch {
50
+ return false;
51
+ }
52
+ };
53
+
39
54
  app.use("/*", cors({
40
55
  origin: (origin) => {
41
- if (!isProduction) return origin || "*";
42
- return allowedOrigins.includes(origin) ? origin : null;
56
+ if (isProduction) return allowedOrigins.includes(origin) ? origin : null;
57
+ if (!origin) return "*";
58
+ return isLocalhostOrigin(origin) ? origin : null;
43
59
  },
44
60
  credentials: true
45
61
  }));
@@ -149,7 +165,11 @@ pass: env.SMTP_PASS! }
149
165
  process.on("exit", cleanup);
150
166
 
151
167
  logger.info(`API running at http://localhost:${actualPort}`);
152
- logger.info(`API docs at http://localhost:${actualPort}/api/swagger`);
168
+ // Docs are only mounted once there is something to document; with no
169
+ // servable tables the URL would 404, so don't advertise it.
170
+ if (backend.collectionRegistry.getCollections().length > 0) {
171
+ logger.info(`API docs at http://localhost:${actualPort}/api/swagger`);
172
+ }
153
173
  } else {
154
174
  server.listen(PORT, () => {
155
175
  logger.info(`API running at http://localhost:${PORT}`);
@@ -5,6 +5,7 @@
5
5
  "moduleResolution": "node",
6
6
  "lib": ["ES2022"],
7
7
  "outDir": "./dist",
8
+ "rootDir": "./src",
8
9
  "strict": true,
9
10
  "esModuleInterop": true,
10
11
  "allowSyntheticDefaultImports": true,
@@ -14,5 +15,5 @@
14
15
  "declaration": true,
15
16
  "sourceMap": true
16
17
  },
17
- "include": ["src/**/*", "drizzle.config.ts"]
18
+ "include": ["src/**/*"]
18
19
  }
@@ -7,11 +7,21 @@
7
7
  # Database connection string (required)
8
8
  # You can use PostgreSQL (postgresql://) or MongoDB (mongodb:// or mongodb+srv://).
9
9
  # The backend will automatically detect the database type.
10
- DATABASE_URL=postgresql://rebase:changeme@localhost:5432/rebase?options=-c%20search_path=public
10
+ # sslmode=disable matches the local docker-compose database, which has no TLS;
11
+ # schema tooling (atlas) would otherwise default to requiring SSL. Remove it
12
+ # when pointing at a managed/cloud database.
13
+ DATABASE_URL=postgresql://rebase:changeme@localhost:5432/rebase?options=-c%20search_path=public&sslmode=disable
11
14
  # DATABASE_URL=mongodb://localhost:27017/rebase
12
15
  #
13
- # Local Postgres without SSL? If you see "SSL is not enabled on the server",
14
- # append &sslmode=disable to the connection string, e.g.:
16
+ # Seeing "SSL is not enabled on the server"? Something in your environment
17
+ # asks for SSL almost always a global PGSSLMODE=require
18
+ # (set once for a cloud database, then inherited by every local project), or an
19
+ # sslmode=require copied in with a connection string.
20
+ #
21
+ # Prefer unsetting PGSSLMODE for this project. Rebase deliberately does not
22
+ # downgrade SSL for you when you have asked for it, not even on localhost:
23
+ # quietly ignoring a security setting is worse than a clear failure. To opt out
24
+ # explicitly for a local database, say so:
15
25
  # DATABASE_URL=postgresql://rebase:changeme@localhost:5432/rebase?options=-c%20search_path=public&sslmode=disable
16
26
 
17
27
  # Separate admin connection string for migrations and schema operations (optional)
@@ -5,7 +5,7 @@
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "dev": "tsx watch --watch=\"../config/**/*\" src/index.ts",
8
+ "dev": "tsx watch --include=\"../config/**/*\" --include=\"./functions/**/*\" src/index.ts",
9
9
  "build": "rebase schema generate --collections ../config/collections && tsc",
10
10
  "start": "node dist/backend/src/index.js"
11
11
  },
@@ -15,12 +15,13 @@
15
15
  "@rebasepro/server-postgres": "workspace:*",
16
16
  "@rebasepro/types": "workspace:*",
17
17
  "drizzle-orm": "^0.45.2",
18
- "hono": "^4.12.10",
18
+ "hono": "^4.12.25",
19
19
  "@hono/node-server": "^1.19.12",
20
20
  "pg": "^8.11.3",
21
21
  "ws": "^8.16.0",
22
22
  "dotenv": "^16.0.0",
23
- "zod": "^4.4.3"
23
+ "zod": "^4.4.3",
24
+ "ts-morph": "28.0.0"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@types/pg": "^8.6.5",
@@ -1,4 +1,5 @@
1
1
  import * as dotenv from "dotenv";
2
+ import fs from "fs";
2
3
  import path from "path";
3
4
  import { fileURLToPath } from "url";
4
5
  import { loadEnv } from "@rebasepro/server";
@@ -7,7 +8,35 @@ import { z } from "zod";
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = path.dirname(__filename);
9
10
 
10
- dotenv.config({ path: path.resolve(__dirname, "../../.env") });
11
+ /**
12
+ * Locate the project's `.env` by walking up the directory tree.
13
+ *
14
+ * A fixed relative path can't work in both modes: in dev this file runs from
15
+ * source at `backend/src/env.ts`, but the compiled output lives at
16
+ * `backend/dist/backend/src/env.js` — two directories deeper — so
17
+ * `../../.env` points at a file that doesn't exist in production. Walking up
18
+ * finds the root `.env` from either location.
19
+ */
20
+ function findEnvFile(startDir: string): string | undefined {
21
+ let dir = startDir;
22
+ // eslint-disable-next-line no-constant-condition
23
+ while (true) {
24
+ const candidate = path.join(dir, ".env");
25
+ if (fs.existsSync(candidate)) return candidate;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir) return undefined;
28
+ dir = parent;
29
+ }
30
+ }
31
+
32
+ // `rebase start` sets DOTENV_CONFIG_PATH to the project's .env; honor it first,
33
+ // then fall back to searching up from this file. When neither is found (e.g. a
34
+ // production host that injects env vars directly), skip dotenv entirely and let
35
+ // the real process environment flow through.
36
+ const envPath = process.env.DOTENV_CONFIG_PATH || findEnvFile(__dirname);
37
+ if (envPath && fs.existsSync(envPath)) {
38
+ dotenv.config({ path: envPath });
39
+ }
11
40
 
12
41
  export const env = loadEnv({
13
42
  extend: z.object({
@@ -39,10 +39,26 @@ const allowedOrigins = isProduction
39
39
  })()
40
40
  : [];
41
41
 
42
+ // In dev we still restrict which origins are reflected. Because `credentials`
43
+ // is enabled, reflecting an arbitrary Origin would let any website the
44
+ // developer happens to visit make credentialed cross-origin requests to this
45
+ // dev server (and read the responses) using the developer's session. So dev
46
+ // reflects only localhost origins; requests with no Origin (curl, same-origin)
47
+ // are unaffected.
48
+ const isLocalhostOrigin = (origin: string): boolean => {
49
+ try {
50
+ const { hostname } = new URL(origin);
51
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
52
+ } catch {
53
+ return false;
54
+ }
55
+ };
56
+
42
57
  app.use("/*", cors({
43
58
  origin: (origin) => {
44
- if (!isProduction) return origin || "*";
45
- return allowedOrigins.includes(origin) ? origin : null;
59
+ if (isProduction) return allowedOrigins.includes(origin) ? origin : null;
60
+ if (!origin) return "*";
61
+ return isLocalhostOrigin(origin) ? origin : null;
46
62
  },
47
63
  credentials: true
48
64
  }));
@@ -1,3 +1,5 @@
1
- import usersCollection from "../../users.js";
1
+ // Resolves after `rebase init` copies this file into config/collections/,
2
+ // next to the shared users.ts — not from inside presets/, which never runs.
3
+ import usersCollection from "./users.js";
2
4
 
3
5
  export const collections = [usersCollection];
@@ -1,6 +1,8 @@
1
1
  import productsCollection from "./products.js";
2
2
  import categoriesCollection from "./categories.js";
3
3
  import ordersCollection from "./orders.js";
4
- import usersCollection from "../../users.js";
4
+ // Resolves after `rebase init` copies this file into config/collections/,
5
+ // next to the shared users.ts — not from inside presets/, which never runs.
6
+ import usersCollection from "./users.js";
5
7
 
6
8
  export const collections = [productsCollection, categoriesCollection, ordersCollection, usersCollection];
@@ -66,6 +66,7 @@ roles: ["admin"] }
66
66
  name: "Password Hash",
67
67
  type: "string",
68
68
  columnName: "password_hash",
69
+ excludeFromApi: true,
69
70
  ui: {
70
71
  hideFromCollection: true,
71
72
  disabled: { hidden: true }
@@ -85,6 +86,7 @@ roles: ["admin"] }
85
86
  name: "Email Verification Token",
86
87
  type: "string",
87
88
  columnName: "email_verification_token",
89
+ excludeFromApi: true,
88
90
  ui: {
89
91
  hideFromCollection: true,
90
92
  disabled: { hidden: true }
@@ -3,8 +3,7 @@ import React from "react";
3
3
  import "@fontsource/jetbrains-mono";
4
4
  import "@fontsource/rubik";
5
5
 
6
- import { useRebaseAuthController } from "@rebasepro/app";
7
- import { Rebase, RebaseAuth } from "@rebasepro/app";
6
+ import { Rebase, RebaseAuth, useRebaseAuthController } from "@rebasepro/app";
8
7
  import { RebaseAdmin, RebaseShell } from "@rebasepro/admin";
9
8
  import { ErrorBoundary } from "@rebasepro/ui";
10
9
  import { RebaseStudio } from "@rebasepro/studio";
@@ -0,0 +1,31 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build output
5
+ dist/
6
+ build/
7
+
8
+ # Environment (secrets — .env.example is tracked)
9
+ .env
10
+ .env.local
11
+ !.env.example
12
+
13
+ # IDE
14
+ .idea/
15
+ .vscode/
16
+ *.swp
17
+ *.swo
18
+
19
+ # OS
20
+ .DS_Store
21
+ Thumbs.db
22
+
23
+ # Drizzle
24
+ drizzle/meta/
25
+
26
+ # Uploads
27
+ uploads/
28
+
29
+ # Rebase dev
30
+ .rebase-dev-url
31
+ .rebase-dev-port
@@ -0,0 +1,10 @@
1
+ # Ensure pnpm links local workspace packages when version specifiers
2
+ # like "*" are used (instead of the pnpm-specific "workspace:*" protocol).
3
+ # This makes the project compatible with both pnpm and npm workspaces.
4
+ link-workspace-packages=true
5
+
6
+ # Disable automatic dependency check before running scripts to prevent background install failures in read-only environments
7
+ verify-deps-before-run=false
8
+
9
+ # Prevent pnpm from prompting for confirmation when purging modules in non-TTY environments (e.g. Docker, CI)
10
+ confirm-modules-purge=false