@protobase/cli 0.66.10 → 0.66.12

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.10";
2
+ var version = "0.66.12";
3
3
  //#endregion
4
4
  export { version };
@@ -22,5 +22,14 @@ const builtInModels = (config, models) => {
22
22
  views: [...webhooks.views, ...added.views].map((view) => view.toModel())
23
23
  };
24
24
  };
25
+ /**
26
+ * A missing index on a built-in table as a recommendation: the app owns its schema, so a database migrated by an earlier
27
+ * version still serves, and only the reads that need the index are refused until `protobase migration` writes it.
28
+ */
29
+ const builtInIndexIssues = (issues) => issues.map((issue) => ({
30
+ ...issue,
31
+ severity: "warning",
32
+ suggestion: `write it as a migration with \`protobase migration\` (${issue.suggestion})`
33
+ }));
25
34
  //#endregion
26
- export { builtInModels };
35
+ export { builtInIndexIssues, builtInModels };
@@ -3,7 +3,7 @@ import { connect } from "../db/connect.js";
3
3
  import { introspect } from "../db/introspect.js";
4
4
  import { loadDataConfig } from "../project/load-data-config.js";
5
5
  import { readOnlyDb } from "../db/read-only-db.js";
6
- import { builtInModels } from "./built-in-models.js";
6
+ import { builtInIndexIssues, builtInModels } from "./built-in-models.js";
7
7
  import { checkResource } from "./check.js";
8
8
  import { indexIssues } from "./index-issues.js";
9
9
  import { readShapes } from "./read-shapes.js";
@@ -28,7 +28,7 @@ const runDoctor = async (options, out) => {
28
28
  views: [...builtIn.views, ...config.views],
29
29
  pages: config.pages
30
30
  });
31
- const indexes = await Promise.all(models.map(async (model) => indexIssues(model, await tableIndexes(db, model), shapes))).finally(() => db.destroy());
31
+ const indexes = await Promise.all(models.map(async (model) => indexIssues(model, await tableIndexes(db, model), shapes))).then((found) => found.map((issues, i) => i < builtIn.models.length ? builtInIndexIssues(issues) : issues)).finally(() => db.destroy());
32
32
  const issues = models.flatMap((model, i) => [
33
33
  ...checkResource(model, tables),
34
34
  ...relationIssues(model, models),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/cli",
3
- "version": "0.66.10",
3
+ "version": "0.66.12",
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.10",
47
- "@protobase/layout": "0.66.10",
48
- "@protobase/query": "0.66.10",
49
- "@protobase/schema": "0.66.10",
50
- "@protobase/server": "0.66.10",
51
- "@protobase/ui": "0.66.10",
46
+ "@protobase/config": "0.66.12",
47
+ "@protobase/layout": "0.66.12",
48
+ "@protobase/query": "0.66.12",
49
+ "@protobase/schema": "0.66.12",
50
+ "@protobase/server": "0.66.12",
51
+ "@protobase/ui": "0.66.12",
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",
@@ -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.
@@ -107,7 +107,7 @@ protobase doctor [connection string | --env VAR] [--config <dir>]
107
107
 
108
108
  The URL comes as for [`scaffold`](#scaffold): the connection string, the variable `--env` names, or `DATABASE_URL`, each also read from the nearest `.env`.
109
109
 
110
- Imports `config/index.ts` and the views of `config/*/ui.ts`, as the config module has them by convention, and checks every exported resource against the database, with the resources Protobase adds by what `protobase.config.ts` (read with `command: 'tool'`) and the resources enable: the [webhook](webhooks.md#subscriptions-in-the-admin-app) ones of `webhooks`, Failed jobs, [Activity](history.md) and [Comments](comments.md). The tables [`migration`](#migration) writes for those have every index their reads need, so after the project's migrations they pass:
110
+ Imports `config/index.ts` and the views of `config/*/ui.ts`, as the config module has them by convention, and checks every exported resource against the database, with the resources Protobase adds by what `protobase.config.ts` (read with `command: 'tool'`) and the resources enable: the [webhook](webhooks.md#subscriptions-in-the-admin-app) ones of `webhooks`, Failed jobs, [Activity](history.md) and [Comments](comments.md). The tables [`migration`](#migration) writes for those have every index their reads need, so after the project's migrations they pass; a read of theirs that lacks an index, as on a table an earlier version wrote, is a warning to run `migration`, since the app owns its schema:
111
111
 
112
112
  - error: missing table or column, field type that does not fit the column (a file field needs a text column), table without a usable key, configured key not backed by a unique index, `.openExisting()` fields that are not the columns of a unique index, relation to a resource the config does not declare
113
113
  - error: a read the [scan guard](api.md#safety) refuses, because no index serves a column it filters, sorts, searches or counts by. Doctor reads each table's indexes from the catalog, as the server does, and judges with the scan guard's own rule every read the API takes and the app makes: a filter on each `.filterable()` field, a sort by each `.sortable()` one, `search()` over the search fields, a role's own records (`.owner()`) and the names a relation shows; each view's list in its order and with the filter it opens with, its filter widgets and their counts, its chart, its map, board, calendar and timeline, its card photos, its order by hand, its related sections, galleries and charts, its named lists, its recent records and a saved search's new records; and each block of a [composed page](layouts.md) that lists records, and each `Chart` block's grouped and split fields. Each error is an index to add, with every request that needs it and where it comes from:
@@ -64,7 +64,7 @@ Once a resource takes comments, admins get **Comments** (the built-in resource `
64
64
 
65
65
  ## The table
66
66
 
67
- [`protobase migration`](cli.md#migration) adds the schema `comments` with the table `comments.comments`; apply it with the project's other migrations. It has an index for each column the Comments list filters on, and the next migration adds one an earlier version's table lacks. `protobase dev` and `protobase serve` do not start while a resource has comments and the table or one of its indexes is missing. A deleted record's comments stay in the table.
67
+ [`protobase migration`](cli.md#migration) adds the schema `comments` with the table `comments.comments`; apply it with the project's other migrations. It has an index for each column the Comments list filters on, and the next migration adds one an earlier version's table lacks. `protobase dev` and `protobase serve` do not start while a resource has comments and the table is missing; without an index added since, they start, and only the Comments list's reads that need it are refused. A deleted record's comments stay in the table.
68
68
 
69
69
  | Column | |
70
70
  | --- | --- |
@@ -71,7 +71,7 @@ Once a resource has history, admins get **Activity** (the built-in resource `rec
71
71
 
72
72
  ## The table
73
73
 
74
- [`protobase migration`](cli.md#migration) adds the table `changes.history` to the `changes` schema of the event capture (renamed with `events: { schema }`), and the capture triggers of each resource with history; apply it with the project's other migrations. It has an index for each column the Activity list filters on, and the next migration adds one an earlier version's table lacks. `protobase dev` and `protobase serve` do not start while the table, one of its indexes or a trigger is missing. A resource that drops `.history()` loses its triggers, unless it has events, at the next migration; its changes stay in the table.
74
+ [`protobase migration`](cli.md#migration) adds the table `changes.history` to the `changes` schema of the event capture (renamed with `events: { schema }`), and the capture triggers of each resource with history; apply it with the project's other migrations. It has an index for each column the Activity list filters on, and the next migration adds one an earlier version's table lacks. `protobase dev` and `protobase serve` do not start while the table or a trigger is missing; without an index added since, they start, and only the Activity list's reads that need it are refused. A resource that drops `.history()` loses its triggers, unless it has events, at the next migration; its changes stay in the table.
75
75
 
76
76
  | Column | |
77
77
  | --- | --- |
@@ -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.
@@ -116,7 +116,7 @@ It adds two pages, over a `webhooks` schema that [`protobase migration`](cli.md#
116
116
  - **Webhook subscriptions** (`webhook_subscriptions`, the table `webhooks.subscriptions`): a name, the events (`orders.created orders.large`, or `orders.*`), the URL, a filter, extra headers, full or thin payload, and whether it is enabled. The server sets the tenant and the owner, the person who made it, and makes the secret, which only a [reveal](api.md#sensitive-fields-and-audit) shows. Saving checks the events and the filter against the resources, and refuses a URL whose host is a private, loopback or link-local address.
117
117
  - **Webhook deliveries** (`webhook_deliveries`, a view over the delivery jobs): the deliveries that wait for their next try or failed for good, with their last error. A delivery leaves the list once it succeeds.
118
118
 
119
- The migration gives the subscriptions table an index for each sort, filter and search of its list, and creates the `pg_trgm` extension the search's indexes need (`create extension if not exists pg_trgm`, a trusted extension since Postgres 13, so any role that may create in the database can). A database that has an earlier version's schema gets the indexes from the next `protobase migration`; the server does not start until it is applied.
119
+ The migration gives the subscriptions table an index for each sort, filter and search of its list, and creates the `pg_trgm` extension the search's indexes need (`create extension if not exists pg_trgm`, a trusted extension since Postgres 13, so any role that may create in the database can). A database that has an earlier version's schema gets the indexes from the next `protobase migration`; until it is applied the server still starts, and refuses only the list's reads that need them.
120
120
 
121
121
  Both are scoped to the tenant like any resource with `.tenant()`, and their access decides who may do what: `webhook_subscriptions.create`, `webhook_deliveries.update` and so on, as for the app's own resources. A role's wildcard over resources, such as `*.read`, grants none of them: their URLs and secrets let whoever reads them call the receivers, so with roles only admins (`*`) and roles that [name them](access.md#resources-that-need-named-grants) manage webhook subscriptions.
122
122