You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 5d156f7
Browse filesBrowse the repository at this point in the historyBrowse files
A `record:details` section heading can show how many values a multi-value field holds on the current record: `sections[].count: { field }`, with the section's `label` writing the `{{ count }}` hole where the number goes.
7
+
8
+
Clause-②: yes (widening)
9
+
10
+
```ts
11
+
{
12
+
name: 'plan_gap',
13
+
label: '{{ count }} features need a higher plan',
14
+
fields: ['capabilities_not_on_plan'],
15
+
count: { field: 'capabilities_not_on_plan' },
16
+
}
17
+
```
18
+
19
+
-**`@objectstack/spec`.**`RecordDetailsProps.sections[]` accepts `count`, a closed object whose one key, `field`, is a bare machine name (snake_case, no dotted path). A section that declares `count` must write the `{{ count }}` hole in its `label`, in every locale of an inline label map. It is refused when it has no `label`, when the label has no hole, when the label writes any other `{{ … }}` hole (another variable, or the count with a formatter), when it writes the single-brace `{count}` / `${count}`, and beside `group` (a group-referencing section's heading is the group's own label). A translated `objects.<object>._sections.<name>.label` writes the same `{{ count }}` hole, and `os i18n extract` seeds it from the authored label. `SECTION_HEADING_COUNT_HOLE` exports the hole's grammar (inner whitespace is free: `{{count}}` is the same hole).
20
+
-**`@objectstack/lint`.** The page field-binding pass resolves `count.field` against the bound object, at error severity, on `os validate`, `os lint` and `os build`: a field the object does not declare is `page-field-unknown`, and a declared field that holds one value is the new `page-section-count-not-multi-value`. A multi-value field is `multiselect`, `checkboxes` or `tags`, or a `select`, `lookup`, `user`, `file` or `image` field with `multiple: true`. A section's `fields[]` miss stays a warning.
21
+
-**Not rendered yet.** The key is declared ahead of its renderer and its description carries the `[EXPERIMENTAL — not enforced]` marker: until the record detail renderer reads it, the hole renders as literal text.
22
+
-**Nothing to migrate.** No stack that parsed before is refused: every new refusal needs the new key. A `{{ count }}` hole in a section that declares no `count` is still accepted.
`GET /api/v1/auth/me/permissions` answers "no capabilities", not "capabilities unknown", when it fails after the caller is known
6
+
7
+
Clause-②: no
8
+
9
+
Once the handler knows who the caller is, it has two ways to fail. When the permission-set resolution rejects, it already answered a closed body: `systemPermissions: []`, so every capability gate closes. Any other throw inside the handler (the effective-object merge, or a resolver that throws synchronously) was caught by an outer `catch` that answered `{ authenticated: true, userId, objects: {}, fields: {} }`. That body has no `systemPermissions`, which is the shape a deployment predating ADR-0066 sends, and the console reads it as "capabilities unknown" and fails open on it. So a server-side fault offered authoring handles the server then refused.
10
+
11
+
The outer `catch` now answers the closed body, built by the same function the success path uses:
12
+
13
+
-`authenticated: true`, the caller's `userId`, and `tenantId` (the session's, or `null`), which this body did not carry before;
14
+
-`positions`, `permissionSets` and `systemPermissions` as `[]`, and `objects`, `fields` and `tabPermissions` as `{}`.
15
+
16
+
The log line is unchanged: `[hono] /auth/me/permissions failed` at `warn`, once.
17
+
18
+
**What does not change.** The success body is byte-identical. The resolver-rejection path still reports the session's positions. A stack with no SecurityPlugin or no metadata service still answers its deliberate open body without `systemPermissions`, since there the server enforces nothing either. A caller with no session still gets `{ authenticated: false }`. No accept set, export or `spec` key moves, and the console needs no change.
Copy file name to clipboardExpand all lines: content/docs/references/ui/component.mdx
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1694,7 +1694,7 @@ View filter rule
1694
1694
| :--- | :--- | :--- | :--- |
1695
1695
|**columns**|`Enum<'1' \| '2' \| '3' \| '4'>`| optional (default: `"2"`) | Number of columns for field layout (1-4) |
1696
1696
|**layout**|`never`| optional |[REMOVED]`record:details` property `layout` was removed in @objectstack/spec 17.0.0 (ADR-0087 D2) — its declared `auto`\|`custom` semantics were never implemented: the renderer tests `layout` only against `inline`\|`compact`, two values the schema never permitted, so both legal values took the same branch and the key selected nothing. Delete the key — the body is already chosen by what you author: `sections` renders the explicit groups (the old `custom`), and omitting it falls back to the object's `highlightFields` (the old `auto`). Run `os migrate meta --from 16` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
1697
-
|**sections**|`{ name?: string; label?: string \| Record<string, string>; columns?: integer; group?: string; … }[]`| optional | Field groups rendered as the detail body, in order. Object form: `{ name?, label?, columns?, fields, hideEmpty?, collapsible?, showBorder?, defaultCollapsed?, icon?, description?, headerColor?}` — or the group-reference form `{ group, columns?, hideEmpty?, showBorder?, headerColor? }`, which inherits members and presentation from the object's `fieldGroups` entry (ADR-0085 §5). |
1697
+
|**sections**|`{ name?: string; label?: string \| Record<string, string>; columns?: integer; group?: string; … }[]`| optional | Field groups rendered as the detail body, in order. Object form: `{ name?, label?, columns?, fields, hideEmpty?, collapsible?, showBorder?, defaultCollapsed?, icon?, description?, headerColor?, count? }` — or the group-reference form `{ group, columns?, hideEmpty?, showBorder?, headerColor? }`, which inherits members and presentation from the object's `fieldGroups` entry (ADR-0085 §5). `count: { field }` shows how many values a multi-value field holds in the heading, through its `{{ count }}` hole. |
1698
1698
|**fields**|`string[]`| optional | Explicit field list to display (optional, overrides highlightFields) |
1699
1699
|**hideFields**|`string[]`| optional | Field names to omit from the body — applied to `fields` and to every section's `fields` (used to dedupe fields already shown in `record:highlights` or as the page title) |
1700
1700
|**inlineEdit**|`boolean`| optional | Allow inline field editing in the detail body (renderer default: on, where the object itself is editable — set `false` to force it off). |
@@ -1720,6 +1720,7 @@ View filter rule
1720
1720
|**icon**|`string`| optional | Heading icon, as a lucide icon name (kebab-case, e.g. `building-2`). A value that is not an ASCII identifier (emoji, CJK text) renders as literal text beside the heading instead. Shown where the section heading renders: a titled section, or any collapsible section. |
1721
1721
|**description**|`string`| optional | Sub-heading text rendered under the section heading (plain string — the renderer applies no translation to it, unlike `label`). Renders on a titled or collapsible section; a collapsible section hides it while collapsed. |
1722
1722
|**headerColor**|`Enum<'muted' \| 'muted/50' \| 'accent' \| 'primary/10' \| 'secondary/10' \| 'destructive/10'>`| optional | Section-header background tint, from the closed six-token vocabulary rendered by objectui's `record:details` header (`muted`\|`muted/50`\|`accent`\|`primary/10`\|`secondary/10`\|`destructive/10`). A value outside the enum is refused at authoring time rather than silently not painting. Omit for an untinted header. |
1723
+
|**count**|`{ field: string }`| optional |[EXPERIMENTAL — not enforced] Show how many values a multi-value field holds on the current record in this section's heading: `{ field: 'tags' }`, with `label` writing the `{{ count }}` hole where the number goes (`'{{ count }} tags'`). The field must be a multi-value field the bound object declares — `multiselect`, `checkboxes`, `tags`, or a `select` / `lookup` / `user` / `file` / `image` field with `multiple: true` — and an absent or empty value counts 0. Every locale of an inline label map, and a translated `objects.<object>._sections.<name>.label`, writes the same `{{ count }}` hole. Not allowed beside `group`. Declared ahead of its renderer: until the record detail renderer reads it, the hole renders as literal text. |
0 commit comments