v0.9.0
Breaking
-
Entity → Entity vocabulary rename — The
Entitynoun has been removed from the entire public API surface. Every type, hook, component, prop, config key, and wire-protocol message that previously used "entity" now uses "entity" (or, in backend callbacks, the flat database term "row"). This is a search-and-replace-level migration for consumers — no behavioral changes. The full rename map follows.Types (
@rebasepro/types)| Old Name | New Name |
|----------|----------|
|Entity<M>|Entity<M>|
|EntityCollection<M>|CollectionConfig<M>|
|EntityCallbacks<M>|CollectionCallbacks<M>|
|EntityValues<M>|EntityValues<M>|
|EntityStatus|EntityStatus|
|EntityReference|EntityReference|
|EntityView|EntityCustomView|
|EntityAction<M>|EntityAction<M>|
|EntityActionClickProps<M>|EntityActionClickProps<M>|
|EntityFormProps|EntityFormProps|
|EntitySidePanelProps|EntitySidePanelProps|
|SideEntityController|SideEntityController|
|EntitySelectionProps|EntitySelectionProps|
|EntityPreview|EntityPreview|
|EntityCollectionView|DataCollectionView|
|EntityCard|EntityCard|
|EntitySelectionTable|EntitySelectionTable|Collection config props
| Old Prop | New Prop |
|----------|----------|
|entityViews|entityViews|
|entityActions|entityActions|
|openEntityMode|openEntityMode|
|includeEntityLink|includeEntityLink|
|entityId(in panel props) |entityId|React hooks & components (
@rebasepro/admin)| Old Name | New Name |
|----------|----------|
|useSideEntityController()|useSideEntityController()|
|useEntitySelectionDialog()|useEntitySelectionDialog()|
|SideEntityProvider|SideEntityProvider|
|mergeEntityActions()|mergeEntityActions()|
|resolveEntityAction()|resolveEntityAction()|
|resolveEntityView()|resolveEntityView()|
|editEntityAction|editEntityAction|
|copyEntityAction|copyEntityAction|
|deleteEntityAction|deleteEntityAction|Callback API (
CollectionCallbacks) — beyond the rename, the parameter shapes changed:| Old Param | New Param | Notes |
|-----------|-----------|-------|
|entity(inafterRead) |row| Now a flatRecord<string, unknown>, not aEntity<M>wrapper |
|entityId(in save/delete) |id|string \| number|
|previousEntity|previousValues|Partial<EntityValues<M>>|
|afterCreate/afterUpdate|afterSave| Usestatus: "new" \| "existing"to distinguish |Migration example:
-import type { EntityCallbacks } from "@rebasepro/types";
-const callbacks: EntityCallbacks = {
- afterRead: ({ entity }) => {
- return { ...entity, values: { ...entity.values, email: "***" } };
- },
- afterCreate: ({ entity }) => { /* ... */ },
- beforeDelete: ({ entityId }) => { /* ... */ },
+import type { CollectionCallbacks } from "@rebasepro/types";
+const callbacks: CollectionCallbacks = {
+ afterRead: ({ row }) => {
+ return { ...row, email: "***" };
+ },
+ afterSave: ({ id, status }) => { if (status === "new") { /* ... */ } },
+ beforeDelete: ({ id }) => { /* ... */ },
};WebSocket wire protocol
| Old Message Type | New Message Type |
|---|---|
FETCH_ENTITY |
FETCH_ONE |
SAVE_ENTITY |
SAVE |
DELETE_ENTITY |
DELETE |
COUNT_ENTITIES |
COUNT |
subscribe_entity |
subscribe_one |
collection_entity_patch |
collection_patch |
Database schema
| Old Name | New Name |
|---|---|
rebase.entity_history (table) |
rebase.entity_history |
entity_id (column) |
entity_id |
rebase_entity_changes (PG NOTIFY channel) |
rebase_entity_changes |
-
Unified
<Rebase>data props — Removed thedataanddriverprops. There are now exactly two ways to provide data:client(server transport) anddataSources(everything else). AdataSourcesentry keyed"(default)"with adriverreplacesclient.dataas the default source — this is how a fully client-side app (e.g. Firestore-only viaRebaseFirebaseApp) is wired. Migration:driver={x}→dataSources={[{ key: "(default)", engine: "firestore", driver: x }]};data={x}had no known users (custom backends implementDataDriver, now the documented integration SPI). -
Deterministic default-source resolution — The default data source resolves as:
"(default)"-keyed entry with driver →client.data→ the sole registered source. Several sources without an explicit default now throw instead of silently picking the first object entry (order-dependent). -
Side-panel / Edit-view / Collection-view component rename — Renames mechanically-generated "Entity" component names to descriptive, role-based names. Components bound to Rebase core data use the
Bindingsuffix. This is a search-and-replace migration — no behavioral changes.Types (
@rebasepro/types)| Old Name | New Name |
|----------|----------|
|EntitySidePanelProps|SidePanelBindingProps|
|sideEntityController(onRebaseContext) |sidePanelController|
|sideEntityController(onEntityActionClickProps) |sidePanelController|
|"Entity.FormActions"(override key) |"EditView.FormActions"|
|"Entity.DetailView"(override key) |"DetailView"|
|"Entity.Preview"(override key) |"RecordPreview"|Components (
@rebasepro/admin)| Old Name | New Name |
|----------|----------|
|SideEntityProvider|SidePanelProvider|
|EntitySidePanel|SidePanelBinding|
|EntityEditView|EditViewBinding|
|EntityEditViewFormActions|EditFormActions|
|EntityDetailView|DetailViewBinding|
|EntityView|RecordViewBinding|
|EntityPreview|RecordPreviewBinding|
|EntityJsonPreview|JsonPreviewBinding|
|DataCollectionView|CollectionViewBinding|
|EntityCollectionBoardView|CollectionBoardViewBinding|
|EntityCollectionCardView|CollectionCardViewBinding|
|EntityCollectionListView|CollectionListViewBinding|
|DataCollectionViewActions|CollectionViewActions|
|DataCollectionViewStartActions|CollectionViewStartActions|
|DataCollectionTable|CollectionTableBinding|
|EntityCollectionRowActions|CollectionRowActions|
|EntitySelectionTable|SelectionTableBinding|
|EntityBoardCard|BoardCardBinding|
|EntityCard|RecordCardBinding|
|useEntityPreviewSlots|usePreviewSlots|
|SideEntityControllerContext|SidePanelControllerContext|Bridge key (
@rebasepro/core)| Old Key | New Key |
|---------|---------|
|"sideEntityController"|"sidePanelController"|
|sideEntityController(onStudioBridge) |sidePanelController| -
Client split into server/browser variants —
RebaseClientis now split so the RLS-bypassing accessor is explicit: userebase.dataAsAdmin(server-only) for admin-scoped, RLS-bypassing access, andrebase.datafor user-scoped access. The public API surface was curated to hide internal plumbing. -
update/deletethrow on not-found — SDKupdate()anddelete()now throw when the target row does not exist, instead of silently returningundefined. -
deleteAllis now internal — removed from the public data accessors. -
Scaffold defaults to cookie auth — new projects store the refresh token in an httpOnly cookie (
authFlowMode: "cookie") by default. -
AdminUser.provider→providerId— renamed to match the canonicalUsertype.
Features & Improvements
-
Membership / relational RLS predicate (
policy.existsIn) — a first-class access predicate for scoping reads/writes by membership in a related collection (e.g. "only rows whose team the caller belongs to"). Compiles to a single correlatedEXISTSsubquery — no per-rowafterReadlookups. Addspolicy.existsIn({ collection, where })and thepolicy.outerField(name)operand for correlating the subquery to the outer row. -
Built-in email → user lookup for invites — opt-in
auth.allowUserLookupexposes an authenticatedPOST /auth/find-userand a clientrebase.auth.findUserByEmail(email)that returns a minimal public profile (uid,displayName,photoURLonly). Removes the hand-rolleddataAsAdminserver function that invite flows previously required. Off by default (enables user enumeration by signed-in users). -
Mount the admin under a path prefix —
RebaseCMSaccepts abasePathso the admin can live under a sub-path route (e.g./admin) without the collection data-grid hanging on URL↔collection resolution. -
Filter operators — LIKE family (
like,ilike, etc.) and null checks, with engine-aware, customizable filter fields. -
Scoped storage tokens — storage access is now governed by scoped, time-limited tokens, with a documented public-files + scoped-token URL model.
-
Uniform server error envelope — server error responses are routed through a central handler for a consistent
{ error: { message, code, details? } }wire shape. -
Inferred data-source transport —
DataSourceDefinition.transportis now optional: entries with a client-sidedriverdefault to"direct", entries without to"server". A"(default)"-keyed entry without a driver can be used to declare the default source's engine/capabilities while the client keeps serving the data. -
installShutdownHandlers— New@rebasepro/server-corehelper that encapsulates graceful shutdown: drains viabackend.shutdown(), runsonCleanup(e.g. closing your database pool), guards against repeated signals, and force-exits if shutdown hangs. Replaces the hand-rolled ~40-line shutdown block in the backend templates — the CLI template previously lacked the re-entry guard and force-exit timer entirely. -
Honest Realtime Meta — Added
FindResponse.meta.estimatedflag on realtime first-paint updates. Whenlisten()emits its immediate heuristic metadata, the emission now carriesestimated: true. Redundant second emissions are skipped when the authoritative count matches the heuristic, and count failures no longer silently pretend to be authoritative — theestimatedflag remains as the signal.
Fixes
-
Concurrency-safe refresh-token rotation — token rotation now uses an atomic
INSERT … ON CONFLICT DO UPDATEinstead of a DELETE-then-INSERT. Concurrent/refreshcalls (which cookie-mode boot can fire at once) previously raced into aunique_device_sessionviolation and returned 500, breaking the session. The client also single-flights concurrent refreshes. -
Cookie session restore —
/auth/refreshnow returns the user object, and the client restores the user (falling back to/me) instead of leaving a blankuid. A cold start restored from an httpOnly cookie alone no longer yields an empty user. -
Resilient auto-refresh — a transient refresh failure (network blip, backend restart, 5xx) now retries with exponential backoff instead of immediately signing the user out; only a genuine auth failure (401/403/invalid/expired token) or exhausted retries signs out.
-
server-postgresqlshipssrc/— the driver package now packssrcalongsidedist, fixing✗ Could not find CLI entry point for @rebasepro/server-postgresqlforrebase db push/schema generatein published/packed installs (the CLI runssrc/cli.tsvia tsx; nodist/cli.jsis built). -
Malformed request bodies — the API now rejects malformed JSON bodies with
400and tightens the public-path check. -
Auth collection callbacks warning — the server warns at startup when an auth collection defines
beforeSave/afterSave/beforeDelete/afterDelete, since auth-driven user creation bypasses the collection save pipeline (use theafterUserCreateauth hook instead). -
CLI DX — friendly diagnostics for "SSL is not enabled on the server" (suggests
sslmode=disable) and for dependency-drop failures that leave a schema half-migrated; a clear warning when--collectionsresolves to a missing path; andrebase devnow surfaces when it overrides the project's.envPORT /VITE_API_URLwith its derived per-project port. -
Scaffold hardening — the frontend Vite config ships
resolve.dedupefor React / React Router so a locallylink:ed Rebase checkout doesn't load duplicate React copies (which broke the admin's data router);.env.exampledocumentssslmode=disable.