@protobase/cli 0.66.9 → 0.66.11

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
1
  //#region package.json
2
- var version = "0.66.9";
2
+ var version = "0.66.11";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/cli",
3
- "version": "0.66.9",
3
+ "version": "0.66.11",
4
4
  "description": "The protobase command: scaffold, doctor, dev, build and serve a Protobase project.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,12 +43,12 @@
43
43
  "dependencies": {
44
44
  "@better-auth/core": "^1.7.12",
45
45
  "@hono/node-server": "^2.1.3",
46
- "@protobase/config": "0.66.9",
47
- "@protobase/layout": "0.66.9",
48
- "@protobase/query": "0.66.9",
49
- "@protobase/schema": "0.66.9",
50
- "@protobase/server": "0.66.9",
51
- "@protobase/ui": "0.66.9",
46
+ "@protobase/config": "0.66.11",
47
+ "@protobase/layout": "0.66.11",
48
+ "@protobase/query": "0.66.11",
49
+ "@protobase/schema": "0.66.11",
50
+ "@protobase/server": "0.66.11",
51
+ "@protobase/ui": "0.66.11",
52
52
  "@react-email/render": "^2.1.0",
53
53
  "@scalar/hono-api-reference": "^0.12.10",
54
54
  "@sentry/vite-plugin": "^11.10.0",
@@ -22,7 +22,7 @@ Sign-in methods, users, roles, organizations and tokens.
22
22
  - [Passkeys](../reference/auth.md#passkeys): People sign in with their device's face or fingerprint unlock, or with a security key.
23
23
  - [Two-factor authentication](../reference/auth.md#two-factor-authentication): A second step with an authenticator app, backup codes or emailed codes.
24
24
  - [Emailed confirmation code](../reference/auth.md#emailed-confirmation-code): Someone without a passkey or a second step confirms it's them with a code emailed on request, where they are, such as before a secret is shown.
25
- - [Confirming an account change](../reference/auth.md#confirming-an-account-change): Adding a passkey, unlinking a provider or deleting the account from an older session asks to confirm it's them in place, never to sign out and sign in again.
25
+ - [Confirming an account change](../reference/auth.md#confirming-an-account-change): Adding a passkey, turning on a second step without a password, unlinking a provider or deleting the account from an older session asks to confirm it's them in place, never to sign out and sign in again; a first passkey takes the password too.
26
26
  - [Sign-in policy](../reference/auth.md#sign-in-policy): Admins choose which sign-in methods are on, off or required, and the server refuses a policy that would lock people out.
27
27
  - [GitHub and social sign-in](../reference/auth.md#sign-in-with-github): People sign in with GitHub or another social provider.
28
28
  - [Sign-in links](../reference/auth.md#sign-in-links-and-connected-accounts): A link signs someone in with a sign-in provider at once, without the sign-in page.
@@ -24,3 +24,5 @@ React components, field types, pages and handlers of your own, loaded as plugins
24
24
  - [Plugin settings](../reference/plugins.md#loading): Plugins are loaded from the config, limited to roles and given settings.
25
25
  - [Plugin fallbacks](../reference/plugins.md#when-a-plugin-is-off): A plugin that fails or does not fit the version is turned off alone, and admins see why.
26
26
  - [Before sign-in](../reference/plugins.md#before-sign-in): A plugin can run around the sign-in page, for example to sign someone in from a link.
27
+ - [Confirm it's you](../reference/plugins.md#api): The app's step that has the person confirm it's them, and its field for a hidden value that asks for it, for your own pages.
28
+ - [Unstable modules](../reference/plugins.md#the-unstable-entry): UI with its own build imports any module of the app's UI, without a promise that it stays the same, instead of copying it.
@@ -272,7 +272,7 @@ Signing out a device stops that device's API token at its next request (see [Whe
272
272
 
273
273
  When the app has mail, the account's address is emailed that its password changed, with a link to choose a new one if it was not them: after a change, a first password, a [reset](#password-reset), an admin setting it, and a new password through the [endpoints for supabase-js](#supabase-js-and-supabasessr).
274
274
 
275
- Checks of the signed-in account's current password are limited to 5 a minute for each account, on top of the limit for each client: changing the password, deleting the account, turning [two-factor authentication](#two-factor-authentication) on or off, its authenticator link and new backup codes, and `POST /api/auth/verify-password`. Every check counts, a right one too; one over the limit is refused with `429` (`TOO_MANY_PASSWORD_CHECKS`, with `X-Retry-After` in seconds), so a stolen session cannot guess the password from many addresses. <gap>The current-password limit cannot be shared between server instances, each of which counts on its own, because a shared store for it is not implemented.</gap>
275
+ Checks of the signed-in account's current password are limited to 5 a minute for each account, on top of the limit for each client: changing the password, deleting the account, turning [two-factor authentication](#two-factor-authentication) on or off, its authenticator link and new backup codes, `POST /api/auth/verify-password`, and [confirming a first passkey](#confirming-an-account-change) with the password. Every check counts, a right one too; one over the limit is refused with `429` (`TOO_MANY_PASSWORD_CHECKS`, with `X-Retry-After` in seconds), so a stolen session cannot guess the password from many addresses. <gap>The current-password limit cannot be shared between server instances, each of which counts on its own, because a shared store for it is not implemented.</gap>
276
276
 
277
277
  On the [account page](#account-page), the `Password` block does both: with a password, it asks for the current one and the new one twice, and says the other devices are signed out; without one, it sets the first, and nobody is signed out. While the sign-in policy turns passwords off, or in a staff session, it only says so.
278
278
 
@@ -369,7 +369,7 @@ People add passkeys (Face ID, Touch ID, Windows Hello, a phone or a security key
369
369
  - On an app with several origins, every passkey works on each of them through [Related Origin Requests](https://passkeys.dev/docs/advanced/related-origins/) (Chrome and Edge 128, Safari 18, Firefox 152; at most 5 registrable domains).
370
370
  - A browser asks for the passkeys of one RP ID at a time. When people have passkeys for more than one of the app's hosts, `GET /api/auth/status` lists them as `passkeyDomains`, the passkey domain first, and the sign-in page adds "Use a passkey made for {host}" for each of the others. `GET /api/auth/passkey/generate-authenticate-options?rpId=<host>` asks for one of them.
371
371
  - A passkey sign-in asks for no second step: the passkey is something you have and something you are or know.
372
- - A session of any age adds a first passkey. Adding another to an account with a passkey or a [second step](#two-factor-authentication), from a session that signed in more than a day ago, asks to [confirm it's them](#confirming-an-account-change) first, and the account page asks for it in place.
372
+ - Adding a passkey from a session that signed in more than a day ago asks to [confirm it's them](#confirming-an-account-change) first, and the account page asks for it in place; for a first passkey, on an account without a [second step](#two-factor-authentication), the account's password does too.
373
373
  - Passkeys can be renamed and removed on the account page; removing one here leaves it on the device.
374
374
 
375
375
  ## Two-factor authentication
@@ -379,7 +379,7 @@ On their account page, people turn on a second step after a password or an email
379
379
  - **Authenticator app:** the page shows a QR code (and the key, to type in), the person confirms it with a code from the app, and gets ten backup codes that each work once instead of a code. New backup codes replace the old ones.
380
380
  - **Emailed code:** with mail configured, a 6-digit code mailed at each sign-in, valid for 10 minutes. An account with an app can also choose an emailed code at sign-in. Backup codes come with the app only.
381
381
 
382
- Turning it on, off, or making new backup codes asks for the account's password when it has one. At sign-in, a correct password or code answers `{ "twoFactorRedirect": true, "twoFactorMethods": ["totp", "otp"] }` instead of a session, and `POST /api/auth/two-factor/verify-totp`, `verify-otp` or `verify-backup-code` with `{ code, trustDevice? }` finishes it; `trustDevice` skips the step in that browser for 30 days. Ten wrong codes in a row lock the account's second step for 15 minutes.
382
+ Turning it on, off, or making new backup codes asks for the account's password when it has one. Without one, turning it on from a session that signed in more than a day ago, on an account without a passkey, asks to [confirm it's them](#confirming-an-account-change) first. At sign-in, a correct password or code answers `{ "twoFactorRedirect": true, "twoFactorMethods": ["totp", "otp"] }` instead of a session, and `POST /api/auth/two-factor/verify-totp`, `verify-otp` or `verify-backup-code` with `{ code, trustDevice? }` finishes it; `trustDevice` skips the step in that browser for 30 days. Ten wrong codes in a row lock the account's second step for 15 minutes.
383
383
 
384
384
  Sign-in with GitHub or another [provider](#sign-in-with-github) is not asked for the second step.
385
385
 
@@ -405,10 +405,13 @@ An account with neither a passkey nor a second step has nothing stronger to conf
405
405
 
406
406
  Better Auth's own check of a fresh session, which could only be met by signing out and in again, is off (`freshAge: 0`). In its place, a session that signed in more than a day ago confirms it's them in place, as above, before a change that needs it, else the change is `403 STRONG_SIGN_IN_REQUIRED`; the confirmation counts for 15 minutes:
407
407
 
408
- - adding a passkey to an account that has a passkey or a second step (a first passkey needs none);
408
+ - adding a passkey;
409
+ - turning on [two-factor authentication](#two-factor-authentication) on an account without a password, a passkey or a second step (an account with a password gives it instead);
409
410
  - unlinking a sign-in provider (`/unlink-account`);
410
411
  - [deleting the account](#deleting-the-account) without a password.
411
412
 
413
+ An account with neither a passkey nor a second step has no strong sign-in to confirm with but an [emailed code](#emailed-confirmation-code), so for its first passkey it may give its password instead, where the account page asks: `POST /api/auth/account/confirm-password` with `{ password }`, `session.account.confirmWithPassword(password)` in the client. It counts for adding that passkey only, within 15 minutes, and is no strong sign-in: a [strong sign-in field](data-config.md#recent-strong-sign-in) still asks for one. A wrong password is `INVALID_PASSWORD`, and every try counts toward the account's [password checks](#name-password-and-devices); an account with a passkey or a second step is refused (`403 PASSWORD_CONFIRMATION_NOT_OFFERED`), and so is a [staff session](#staff-sign-in).
414
+
412
415
  Listing the account's devices (`/list-sessions`) needs no confirmation.
413
416
 
414
417
  ## Sign-in policy
@@ -335,8 +335,14 @@ From `@protobase/ui`:
335
335
  | `FieldWidget`, `FieldWidgetProps`, `FieldWidgetEditorProps` | The types of a [field type](#field-types) and of its parts' props. |
336
336
  | `RecordCardProps` | The props of a [record card](custom-components.md#record-cards), `{ row }`. |
337
337
  | `AuthLayout`, `CodeForm`, `ResendCodeButton`, `Button`, `buttonClass`, `Input`, `Spinner`, `LogoMark` | The pieces of the app's sign-in pages, for a page of a plugin or a host's own that looks like them. Outside the app, `@protobase/ui/styles.css` is the stylesheet that draws them, for a Vite build with Tailwind. |
338
+ | `ConfirmSignIn`, `ConfirmSignInProps` | The app's step that asks the person to confirm it's them, with a passkey, a code or their password, after the server refused something for want of a recent strong sign-in; `onConfirmed` is called once they did, to ask again. |
339
+ | `SensitiveField`, `SensitiveFieldProps` | The app's field for a value hidden like a password, which shows a stored one only once the person asks for it, confirming it's them first when the server wants that. |
338
340
  | `isFramed`, `useArmedAfterFocus`, `useGuardedPress`, `allowDelayMs` | The connect page's guard against clickjacking and click-through, for a host's own consent screen: it refuses to render when `isFramed()`, and its Allow button spreads `useGuardedPress(armed, onAllow)`, so a press counts only once the page has been in front for `allowDelayMs`, and a press anywhere on the page before then starts that wait again. |
339
341
  | `pluginApi` | The plugin API of the app's `@protobase/ui`. |
340
342
  | `protobaseVersion` | The Protobase version of the app's `@protobase/ui`. |
341
343
 
344
+ ### The unstable entry
345
+
346
+ The modules of `@protobase/ui` that the table leaves out are reached one by one at `@protobase/ui/unstable/<module>`, by the module's path in `packages/ui/src` without its extension, such as `@protobase/ui/unstable/primitives/skeleton`. They come with no promise: any release may change or remove them, outside the plugin API. They are for UI that a bundler of its own builds on `@protobase/ui`, such as a host's own pages, so it imports a component rather than copying it; a plugin, or a project's `protobase.ui.tsx`, cannot import them, like any other file inside `@protobase/ui` (see [imports](#writing-a-plugin)). A module an app needs to rely on can become a stable export: see [versioning](versioning.md#plugins).
347
+
342
348
  From `@protobase/layout`: `component(name, { plugin })` (see [above](#declaring-its-components-in-the-config)). From `@protobase/schema`: `.widget(name, props)` on a view's fields and `d.cards(fields, { card })` (see [Views](ui-config.md)). From the config: `plugins`; from the environment: `PROTOBASE_PLUGINS` and `PROTOBASE_PLUGINS_BEFORE_SIGN_IN` (see [loading](#loading)). The tokens a plugin's utilities read are in [Theme](theme.md).
@@ -92,6 +92,10 @@ A project's own `protobase.ui.tsx` is a plugin of its bundle, so the same rule h
92
92
 
93
93
  Releases keep the plugin API, below 1.0 too, where a minor version may change the config: they add exports to `@protobase/ui`, but remove or change one, add or remove a module the app shares with plugins, or move to a new major version of React only with a new plugin API. A test pins every export of `@protobase/ui` (`packages/ui/src/plugins/plugin-api.exports.json`) and fails when one goes away while `pluginApi` stays the same.
94
94
 
95
+ `@protobase/ui/unstable/<module>` reaches every other module of `@protobase/ui`, by its path in `packages/ui/src` without its extension (see [the unstable entry](plugins.md#the-unstable-entry)). It is outside the plugin API and keeps no promise: any release, a patch too, may change or remove what it exports. Making an export stable is one line in `packages/ui/src/index.ts` and its name in `plugin-api.exports.json`, from then on kept like every other.
96
+
97
+ Protobase's own screens are built from the stable entry's parts: a test (`scripts/ui-exports/building-blocks.test.ts`) fails when a component or hook that another folder of `packages/ui/src` imports is neither exported from the stable entry nor named in `packages/ui/src/plugins/unstable-blocks.json`, under `unstable` with why it stays off or under `pending` until that is decided.
98
+
95
99
  ## Runtime updates
96
100
 
97
101
  An app whose host can update its runtime, such as a hosting platform, can show admins an **Update** button in the top bar. `PROTOBASE_RUNTIME_URL` (or `runtimeUpdates.url` in `protobase.config.ts`) names the host's endpoint, and `/api/meta` passes it on as `runtime: { url }` to callers with the `admin` role only. Without it, or for anyone else, the app shows nothing and asks nothing.