@rebasepro/cli 0.16.0 → 0.16.1-canary.g701140a
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/dist/bundle.d.ts +19 -0
- package/dist/commands/cloud/projects.d.ts +14 -4
- package/dist/commands/cloud/resources.d.ts +10 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/function-portability.d.ts +45 -0
- package/dist/index.d.ts +17 -17
- package/dist/index.es.js +4332 -3973
- package/dist/index.es.js.map +1 -1
- package/dist/telemetry/consent.d.ts +1 -1
- package/dist/telemetry/index.d.ts +7 -7
- package/package.json +8 -8
- package/templates/eject/backend/src/index.ts +6 -0
- package/templates/template/README.md +1 -1
- package/templates/template/ai-instructions.md +5 -2
- package/templates/template/backend/functions/hello.ts +43 -22
- package/templates/template/docker-compose.yml +10 -1
- package/templates/template/gitignore +1 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { TelemetryEvent, TelemetryEventName } from "./payload";
|
|
2
|
-
export { TELEMETRY_SCHEMA_VERSION, bucket, durationBucket, errorClass, buildEvent, sanitize } from "./payload";
|
|
3
|
-
export type { TelemetryEvent, TelemetryEventName, TelemetryValue } from "./payload";
|
|
4
|
-
export { configPath, readConfig, writeConfig } from "./identity";
|
|
5
|
-
export type { TelemetryConfig } from "./identity";
|
|
6
|
-
export { readProjectPolicy } from "./project";
|
|
7
|
-
export type { ProjectTelemetryPolicy } from "./project";
|
|
1
|
+
import { TelemetryEvent, TelemetryEventName } from "./payload.js";
|
|
2
|
+
export { TELEMETRY_SCHEMA_VERSION, bucket, durationBucket, errorClass, buildEvent, sanitize } from "./payload.js";
|
|
3
|
+
export type { TelemetryEvent, TelemetryEventName, TelemetryValue } from "./payload.js";
|
|
4
|
+
export { configPath, readConfig, writeConfig } from "./identity.js";
|
|
5
|
+
export type { TelemetryConfig } from "./identity.js";
|
|
6
|
+
export { readProjectPolicy } from "./project.js";
|
|
7
|
+
export type { ProjectTelemetryPolicy } from "./project.js";
|
|
8
8
|
/**
|
|
9
9
|
* Where events go. Overridable so a fork can point at its own collector, and so
|
|
10
10
|
* the tests never touch the network.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.1-canary.g701140a",
|
|
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.16.
|
|
35
|
-
"@rebasepro/client": "0.16.
|
|
36
|
-
"@rebasepro/codegen": "0.16.
|
|
37
|
-
"@rebasepro/server": "0.16.
|
|
38
|
-
"@rebasepro/server-postgres": "0.16.
|
|
39
|
-
"@rebasepro/types": "0.16.
|
|
34
|
+
"@rebasepro/agent-skills": "0.16.1-canary.g701140a",
|
|
35
|
+
"@rebasepro/client": "0.16.1-canary.g701140a",
|
|
36
|
+
"@rebasepro/codegen": "0.16.1-canary.g701140a",
|
|
37
|
+
"@rebasepro/server": "0.16.1-canary.g701140a",
|
|
38
|
+
"@rebasepro/server-postgres": "0.16.1-canary.g701140a",
|
|
39
|
+
"@rebasepro/types": "0.16.1-canary.g701140a"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^26.1.2",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"scripts": {
|
|
67
67
|
"test": "vitest run",
|
|
68
68
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
69
|
-
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json && node ../../scripts/assert-build-output.mjs",
|
|
69
|
+
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.json && node ../../scripts/add-dts-extensions.mjs dist && node ../../scripts/assert-build-output.mjs",
|
|
70
70
|
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
|
|
71
71
|
"typecheck:test": "tsc --noEmit -p tsconfig.test.json"
|
|
72
72
|
}
|
|
@@ -201,6 +201,12 @@ pass: env.SMTP_PASS! }
|
|
|
201
201
|
// import { requireAuth, requireAdmin } from "@rebasepro/server";
|
|
202
202
|
// app.get("/admin/report", requireAuth, requireAdmin, handler);
|
|
203
203
|
//
|
|
204
|
+
// From the package root here, deliberately — **not** from
|
|
205
|
+
// `@rebasepro/server/functions`. The guards on that subpath read an
|
|
206
|
+
// identity that a Rebase router has already resolved, which is the right
|
|
207
|
+
// thing inside `backend/functions/` and useless out here, where no such
|
|
208
|
+
// middleware ran. These verify the token themselves.
|
|
209
|
+
//
|
|
204
210
|
// `requireAuth` answers 401 without a valid token; `requireAdmin` answers
|
|
205
211
|
// 403 without the `admin` role and must follow `requireAuth`. Note that
|
|
206
212
|
// `c.get("driver")` — the driver carrying the caller's identity — is only
|
|
@@ -87,7 +87,7 @@ Drop a Hono app in `backend/functions/` and it's auto-mounted at `/api/functions
|
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
89
|
// backend/functions/hello.ts
|
|
90
|
-
import { defineFunction, requireAuth, requireAdmin } from "@rebasepro/server";
|
|
90
|
+
import { defineFunction, requireAuth, requireAdmin } from "@rebasepro/server/functions";
|
|
91
91
|
|
|
92
92
|
export default defineFunction((app) => {
|
|
93
93
|
// Deliberately public — anyone can call this.
|
|
@@ -15,5 +15,8 @@ rebase skills install
|
|
|
15
15
|
- Step 1: Run `rebase schema generate` to compile collections to the Drizzle schema.
|
|
16
16
|
- Step 2: Run `rebase db push` (development) or `rebase db generate && rebase db migrate` (production) to apply schema changes to the database.
|
|
17
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).
|
|
18
|
-
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` 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 `c
|
|
19
|
-
5. **
|
|
18
|
+
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
|
+
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
|
+
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
|
+
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`.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defineFunction,
|
|
3
|
+
requireAuth,
|
|
4
|
+
requireAdmin,
|
|
5
|
+
getUser,
|
|
6
|
+
getUserId
|
|
7
|
+
} from "@rebasepro/server/functions";
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Example custom function route.
|
|
@@ -11,17 +17,24 @@ import { defineFunction, requireAuth, requireAdmin } from "@rebasepro/server";
|
|
|
11
17
|
* Call from the client SDK:
|
|
12
18
|
* const result = await client.call("functions/hello", { name: "World" });
|
|
13
19
|
*
|
|
14
|
-
* Authored with `defineFunction`, which hands you a pre-typed Hono app
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
20
|
+
* Authored with `defineFunction`, which hands you a pre-typed Hono app (so
|
|
21
|
+
* `c.get("user")` / `c.get("driver")` are typed) and the `rebase` singleton via
|
|
22
|
+
* the injected context — use any Hono middleware, define any HTTP methods,
|
|
23
|
+
* access the request/response directly.
|
|
24
|
+
*
|
|
25
|
+
* **Import from `@rebasepro/server/functions`, not `@rebasepro/server`.** Same
|
|
26
|
+
* code, but that entry point is the portable one: it pulls in nothing that
|
|
27
|
+
* needs Node, so a function written against it can run on any JavaScript
|
|
28
|
+
* runtime. The package root reaches the whole framework — the boot sequence,
|
|
29
|
+
* the file loaders, the WebSocket layer — which is correct for a server
|
|
30
|
+
* entrypoint and is not what a route handler needs.
|
|
18
31
|
*
|
|
19
32
|
* **Custom functions are not authenticated for you.** The functions router
|
|
20
33
|
* parses the caller's token and puts the result in the context, but it does
|
|
21
34
|
* not reject anonymous requests — a webhook receiver (Stripe, GitHub) has no
|
|
22
35
|
* token to send, and that has to keep working. So every route in this folder
|
|
23
|
-
* is public until you say otherwise, and reading `c
|
|
24
|
-
*
|
|
36
|
+
* is public until you say otherwise, and reading `getUser(c)` is not a check:
|
|
37
|
+
* an anonymous caller just gets `undefined` and the handler runs anyway.
|
|
25
38
|
*
|
|
26
39
|
* Say otherwise with `requireAuth` / `requireAdmin`, in the route's own
|
|
27
40
|
* middleware slot as below. `requireAuth` answers 401 without a valid token.
|
|
@@ -35,19 +48,25 @@ import { defineFunction, requireAuth, requireAdmin } from "@rebasepro/server";
|
|
|
35
48
|
* bypass** — your policies are still evaluated, just against that identity. So
|
|
36
49
|
* `policy.serverContext()` is false for it (that arm means "no uid at all"),
|
|
37
50
|
* and anything an `admin` user can reach, it can reach too. For request-scoped
|
|
38
|
-
* data access use c
|
|
39
|
-
* identity. `rebase.sql()` is the real bypass — owner connection, no policies
|
|
40
|
-
* (`rebase` also exposes auth, storage,
|
|
51
|
+
* data access use `getUser(c)` and `getDriver(c)`, which carry the caller's
|
|
52
|
+
* identity. `rebase.sql()` is the real bypass — owner connection, no policies,
|
|
53
|
+
* and the one accessor that is Node-only. (`rebase` also exposes auth, storage,
|
|
54
|
+
* email.)
|
|
55
|
+
*
|
|
56
|
+
* Two habits worth keeping from the start, because both are free here and
|
|
57
|
+
* expensive to retrofit later:
|
|
58
|
+
*
|
|
59
|
+
* - Read configuration **inside** a handler — `requireEnv(c, "STRIPE_KEY")`
|
|
60
|
+
* or `lazyResource(...)` — never `process.env.X` at the top of the file. A
|
|
61
|
+
* module-scope read that comes back undefined takes the whole file down at
|
|
62
|
+
* import time, and the loader reports that only as "skipped".
|
|
63
|
+
* - Wrap work that outlives the response in `waitUntil(c, promise)` rather
|
|
64
|
+
* than leaving a floating promise. It is what lets a graceful shutdown wait
|
|
65
|
+
* for your webhook instead of dropping it.
|
|
41
66
|
*/
|
|
42
67
|
export default defineFunction((app, { rebase }) => {
|
|
43
68
|
void rebase; // available for dataAsAdmin/auth/storage/email — see commented usage below
|
|
44
69
|
|
|
45
|
-
/** The caller's id, or undefined when nobody is signed in. */
|
|
46
|
-
const uidOf = (user: unknown): string | undefined =>
|
|
47
|
-
(typeof user === "object" && user !== null && "uid" in user)
|
|
48
|
-
? (user as { uid?: string }).uid
|
|
49
|
-
: undefined;
|
|
50
|
-
|
|
51
70
|
// ── Public ────────────────────────────────────────────────────────────
|
|
52
71
|
// Deliberately public: no guard, so anyone can call it. That is a fine
|
|
53
72
|
// choice for health probes, webhook receivers and public content — the
|
|
@@ -68,17 +87,17 @@ export default defineFunction((app, { rebase }) => {
|
|
|
68
87
|
// await rebase.email.send({
|
|
69
88
|
// to: "admin@example.com",
|
|
70
89
|
// subject: "Function called",
|
|
71
|
-
// html: `<p>Hello from ${
|
|
90
|
+
// html: `<p>Hello from ${getUserId(c)}!</p>`,
|
|
72
91
|
// });
|
|
73
92
|
//
|
|
74
|
-
// Admin-scoped data (
|
|
93
|
+
// Admin-scoped data (RLS evaluated as the `admin` role — trusted work
|
|
94
|
+
// only):
|
|
75
95
|
// const authors = await rebase.dataAsAdmin.authors.find({ limit: 5 });
|
|
76
|
-
// For user-scoped data (RLS applies), use
|
|
77
|
-
// (c.get("driver")), which carries the caller's identity.
|
|
96
|
+
// For user-scoped data (RLS applies as the caller), use getDriver(c).
|
|
78
97
|
|
|
79
98
|
return c.json({
|
|
80
99
|
message: `Hello, ${body.name || "World"}!`,
|
|
81
|
-
user:
|
|
100
|
+
user: getUserId(c)
|
|
82
101
|
});
|
|
83
102
|
});
|
|
84
103
|
|
|
@@ -86,6 +105,8 @@ export default defineFunction((app, { rebase }) => {
|
|
|
86
105
|
// Order matters: `requireAuth` first (401 for anonymous), then
|
|
87
106
|
// `requireAdmin` (403 for a signed-in non-admin).
|
|
88
107
|
app.get("/stats", requireAuth, requireAdmin, (c) => {
|
|
89
|
-
|
|
108
|
+
// `getUser` returns a narrowed `{ uid, roles, ...claims }` — no cast,
|
|
109
|
+
// and `roles` is always an array.
|
|
110
|
+
return c.json({ admin: getUser(c)?.uid });
|
|
90
111
|
});
|
|
91
112
|
});
|
|
@@ -30,7 +30,16 @@ name: {{PROJECT_NAME}}
|
|
|
30
30
|
services:
|
|
31
31
|
# ── PostgreSQL ───────────────────────────────────────────────────────
|
|
32
32
|
db:
|
|
33
|
-
image
|
|
33
|
+
# pgvector's image rather than the plain `postgres:18-alpine`: it is the
|
|
34
|
+
# official Postgres image with the `vector` extension already built in, on
|
|
35
|
+
# the same major. A `{ type: "vector" }` property compiles to `VECTOR(n)`,
|
|
36
|
+
# which a stock Postgres answers with `type "vector" does not exist` — a
|
|
37
|
+
# boot failure whose cause is an image, not a config. Nothing here installs
|
|
38
|
+
# extensions on your behalf, so the image is where that decision lives.
|
|
39
|
+
#
|
|
40
|
+
# Swapping this for `postgres:18-alpine` is safe on the same major if you
|
|
41
|
+
# never declare a vector property.
|
|
42
|
+
image: pgvector/pgvector:pg18
|
|
34
43
|
restart: unless-stopped
|
|
35
44
|
environment:
|
|
36
45
|
POSTGRES_USER: rebase_app
|