Repository navigation
v0.22.0
Added
-
Linking an account returns the provider's profile picture.
verify
already fetches a photo from every provider that has one, and both sign-in
branches pass it toupdateUser— butPOST /auth/link/:providerasked for
the same profile, got the same answer and dropped it. An app whose users link
an account after signing up therefore had no route to a picture at all,
which is the normal case wherever the account came first. Both branches now
answer withphotoURL, the idempotent already-linked one included, so a
re-link is also a way to refresh it. Returned rather than stored on purpose:
these URLs are signed and expire (LinkedIn's within weeks), so a column
holding one is a column that turns into a broken image later — copy the bytes
somewhere you own while the URL is still good. -
beforeQuerynarrows which rows a read asks for.afterReadsees rows
after they are fetched, so a tenant scope or a per-role row filter could only
be an RLS policy or a condition pushed into every caller. A collection's
callbacks.beforeQuery, or a global one oninitializeRebaseBackend, returns
afilteror alogicalgroup that is AND-ed into the read before it is
compiled. It can only narrow: nothing it returns can drop or replace the
caller's own conditions. It applies to the listing, the single get, the count,
the aggregate, search, vector reads, nested paths, the realtime refetch behind
.listen(), and the rows loaded for a relation or an?include=, where the
target collection's hook is the one that applies. A hook filter that names a
column the table does not have refuses the request with
400 UNKNOWN_FILTER_FIELD, whateverconfigureUnknownFilterFieldsis set to.
Postgres only: a MongoDB or Firestore collection that declares one fails at
boot rather than serving every row. -
search.modecan be"hybrid", which finds substrings and folds accents
in one collection. Full-text search matches whole words, sosebnever
foundSebastianandauditnever foundLead Auditor. The plain search
finds substrings but cannot fold accents, somunoznever foundMuñoz.
mode: "hybrid"ORs the indexed full-text match with an accent-folded
substring match over the samesearch.fields, and ranks full-text hits above
substring-only ones. It changes the query and not the generated column, so it
can be turned on for a live collection without the rebuild a changedsearch
block otherwise needs; it adds theunaccentextension and one helper
function if they are missing. The substring half cannot use the GIN index,
which is why"fts"stays the default. -
Five plugin slots that rendered nowhere now render.
entity.field.beforeandentity.field.afterrender around every form
field,entity.row.actionsin each table row's hover tools,global.search
in the app bar beside the breadcrumbs, andshell.toolbarat the end of the
app bar. All five were declared and documented, and registering for one did
nothing.UNRENDERED_SLOTSis now empty, and a test that scans for render
sites fails if a slot is declared without one again. -
A component override can be named by module path. An override in a
collection'sadmin.componentstook only a component, so declaring one meant
importing React intoconfig/collections/*.ts, which the backend loads too.
Componentnow takes a path or a lazyimport(), asadmin.Fielddoes, and
the collections Vite plugin turns the path into a lazy import. An override
that cannot be resolved renders the built-in component instead of nothing.
Changed
-
The record dialog has its Create and Save buttons at the bottom. In a
collection opened withopenEntityMode: "dialog", such asusersin a
scaffolded project, Create, "Create and close", Save and Discard sat in the
right-hand corner of the top bar. They are now in a footer under the form,
where every other dialog in the admin keeps its buttons. The top bar keeps the
title, the overflow menu and the window controls. The side panel and the
full-screen record still keep Save in the top bar, where it stays in view
while a long form scrolls. -
A form of four fields or fewer is one column. The derived layout paired
fields by type, so the new-user form put Email beside Name and left Roles
alone on the row below. Short forms now stack. Aspanset on a property is
still honoured, and longer forms keep their columns. -
The
entity.row.actionsand field slot props describe what their render
sites have. InEntityRowActionsProps,entityIdisstring | number,
path,collectionandselectionControllerare optional, and
parentCollectionSlugsandparentEntityIdsare gone. In
EntityFieldSlotProps,collectionis optional. A relation picker renders
rows with no collection path, a table can hide selection, and a field can be
rendered outside a collection form. A component typed against the old props
has to handle the optional ones.
Removed
-
PropertyIdCopyTooltipandLabelWithIconAndTooltipare gone from
@rebasepro/cms. UsePropertyKeyHintto show a property's key beside a
label of your own, orLabelWithIconwith its newpropertyKeyprop. -
The
collection.filter-panelanddashboard.widgetslots, with
CollectionFilterPanelPropsandDashboardWidgetProps. Neither ever
rendered. The admin has no filter sidebar for the first to render into; put
filter UI incollection.toolbarorcollection.widgets. The second named no
position on the page; the home page hashome.children.start,
home.children.end,home.cardsandhome.card.widget.
Fixed
-
A search for two words finds a row that keeps them in two fields.
Searchingsebastian melendezon a collection withfirst_nameand
last_namereturned nothing: the default search compiled one
ILIKE '%sebastian melendez%'per column, and no column holds both words.
Typingsebastianreturned nothing either — the trailing space was part of
the pattern. The search string is now split into terms, OR-ed across the
searchable fields and AND-ed across the terms, which is what
websearch_to_tsqueryalready did for a collection that declared asearch
block, so both paths answer the same question. Double quotes keep a run
together as a phrase, a one-word search compiles to exactly the SQL it always
did, and no search that used to match stops matching. Same fix in the Mongo
driver and in the offline evaluator, which had the same shape. -
The service key is no longer rate limited. The data API buckets callers
as API key, signed-in user, then IP — andidentifysaw the service key's
uid: "service"and gave it the ordinary user allowance of 1000 requests per
15 minutes, shared across every service-key caller. It is the deployment's own
credential and already reaches every row, so the limit protected nothing that
was not already open to whoever holds the key; what it stopped was the work
the key exists for. A backfill of 7,925 rows died at row ~1000 with a 429, and
a managed deployment has norateLimitsurface to raise. The service identity
now skips the limiter entirely, without spending the bucket of whoever shares
its key. API keys keep their ownrate_limit, users theuserallowance,
anonymous traffic the IP bucket. -
A refused Google sign-in onto an existing account now tells the visitor
what to do, in their language. Signing in with a provider onto an address
whose account cannot be linked automatically showed the API's message on the
login screen, word for word: "…Sign in with your existing method, then POST
to /auth/link/google to link google to your account."LoginViewnow shows
"An account with this email already exists. Sign in with your email and
password instead." when that account holds a password, and "Sign in the way
you did before." otherwise, in all seven locales. The server's response is
unchanged for API callers, and theEMAIL_NOT_VERIFIEDerror now carries
details: { reason, provider }, wherereasonis
"local-account-unverified"or"provider-email-unverified", so your own
login screen can do the same. -
Starring a collection on the admin home page no longer draws a star the
size of a card. The favourites chip passedsize="small"to a lucide icon,
which writessizestraight into the SVG'swidthandheight.smallis
not a length, so the browser dropped it and the icon filled the chip. The
same mistake made four more icons render oversized: the join-path delete
button in the collection editor's relations tab, the export button, the
empty avatar in the user picker, and "Log in anonymously" on the Firebase
login screen. All five now passiconSize, and a lint rule
(rebase/lucide-icon-numeric-size) refuses a named size on a lucide icon. -
"New user" in the admin saves again when
users.tsdoes not mark the
secret columns. A project's ownuserscollection replaces the default,
and a copy scaffolded beforeexcludeFromApiwas in the template leaves it
offpasswordHashandemailVerificationToken. The server adds the flag at
boot, but the admin built its collections from the file as written, so the
new-user form sent both fields asnull. The server then refused the create:
"'passwordHash', 'emailVerificationToken' are excluded from the API on
'users' and cannot be written through it". Neither field is shown in the
form. EveryCollectionRegistrynow applies the same rule when it copies an
auth collection, so the admin and the server agree on which columns are the
server's, whether or not the file says so. The boot warning asking you to add
excludeFromApi: trueis unchanged. -
Tab moves to the next field in a record dialog or side panel. Tab from a
field could land on the dialog itself, so the next Tab started again from the
header buttons, and Shift+Tab did the same. Leaving the field closed a tooltip
rendered inside the dialog. Radix's focus trap treats any node removed while
focus is between two elements as the focused element being deleted: it
focused the dialog, and the browser abandoned the move. The kit'sDialogand
Sheetnow complete a move interrupted this way, whatever removed the node. -
The property ID no longer covers the form. Text, date, switch, geopoint,
binary and vector fields opened a "Property ID" tooltip over their own label
as soon as they took focus, and again on hover. The key now appears in small
type beside the label when you rest the pointer on it, and clicking it copies
it. It covers nothing, never opens on focus, and is not a Tab stop. -
Global callbacks run. The
callbackspassed toinitializeRebaseBackend,
andexport const callbacksfromconfig/index.tson the managed runtime,
were stored on the backend's own collection registry. No driver reads that
one: each builds a registry of its own at boot and looks for global callbacks
there. So a globalafterRead,beforeSave,afterSave,beforeDeleteor
afterDeletewas accepted and never called, on any data path. The backend now
hands them to every driver at boot, and refuses to start if a driver returns
no registry that can take them. If you declared global callbacks, they start
running with this release — read them again before you upgrade. A global
beforeQuerybeside a MongoDB or Firestore data source also refuses to start. -
Cloud status no longer tells a serving project to create its database.
A managed project whose database the control plane had not recorded showed
blockedOn: no_databasebesidestatus: activeinrebase cloud status,
and suggestedrebase cloud db create --type managed, the one command that
could provision a second, empty database and point the app at it. A project
that has deployed once has a database, so the CLI now reports it blocked on a
missing database only before its first deploy. -
A time imported from Excel keeps its minute. The spreadsheet reader turns
Excel's fractional-day dates into milliseconds by rounding down, so 12:30
often arrived as 12:29:59.999 and a field showing minutes displayed 12:29. A
date within a millisecond of a whole second is now put back on it; a real
sub-second time is left alone.