@rebasepro/cli 0.16.0 → 0.16.1-canary.g2d1aec8

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.
@@ -1,4 +1,4 @@
1
- import { TelemetryEventName } from "./payload";
1
+ import { TelemetryEventName } from "./payload.js";
2
2
  /**
3
3
  * Asking, and what the question looks like.
4
4
  *
@@ -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.0",
3
+ "version": "0.16.1-canary.g2d1aec8",
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.0",
35
- "@rebasepro/client": "0.16.0",
36
- "@rebasepro/codegen": "0.16.0",
37
- "@rebasepro/server": "0.16.0",
38
- "@rebasepro/server-postgres": "0.16.0",
39
- "@rebasepro/types": "0.16.0"
34
+ "@rebasepro/agent-skills": "0.16.1-canary.g2d1aec8",
35
+ "@rebasepro/client": "0.16.1-canary.g2d1aec8",
36
+ "@rebasepro/server": "0.16.1-canary.g2d1aec8",
37
+ "@rebasepro/codegen": "0.16.1-canary.g2d1aec8",
38
+ "@rebasepro/server-postgres": "0.16.1-canary.g2d1aec8",
39
+ "@rebasepro/types": "0.16.1-canary.g2d1aec8"
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.get("user")` 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. **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`.
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 { defineFunction, requireAuth, requireAdmin } from "@rebasepro/server";
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
- * (so `c.get("user")` / `c.get("driver")` are typed) and the `rebase`
16
- * singleton via the injected context — use any Hono middleware, define any
17
- * HTTP methods, access the request/response directly.
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.get("user")` is not a
24
- * check: an anonymous caller just gets `undefined` and the handler runs anyway.
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.get("user") and c.get("driver"), which carry the caller's
39
- * identity. `rebase.sql()` is the real bypass — owner connection, no policies.
40
- * (`rebase` also exposes auth, storage, email.)
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 ${uidOf(c.get("user"))}!</p>`,
90
+ // html: `<p>Hello from ${getUserId(c)}!</p>`,
72
91
  // });
73
92
  //
74
- // Admin-scoped data (bypasses RLS — trusted work only):
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 the request-scoped driver
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: uidOf(c.get("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
- return c.json({ admin: uidOf(c.get("user")) });
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: postgres:18-alpine
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
@@ -29,6 +29,7 @@ uploads/
29
29
  # Rebase dev
30
30
  .rebase-dev-url
31
31
  .rebase-dev-port
32
+ .rebase-dev-secrets.json
32
33
 
33
34
  # Built project bundle (`rebase build`)
34
35
  dist-bundle/